diff --git a/BattlePlace/Rating.hs b/BattlePlace/Rating.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/Rating.hs
@@ -0,0 +1,16 @@
+{-|
+Module: BattlePlace.Rating
+Description: Rating type.
+License: MIT
+-}
+
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module BattlePlace.Rating
+	( Rating(..)
+	) where
+
+import qualified Data.Aeson as J
+
+-- | User rating.
+newtype Rating = Rating Double deriving (Eq, Ord, J.FromJSON, J.ToJSON)
diff --git a/BattlePlace/Skill/Glicko/Types.hs b/BattlePlace/Skill/Glicko/Types.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/Skill/Glicko/Types.hs
@@ -0,0 +1,46 @@
+{-|
+Module: BattlePlace.Skill.Glicko.Types
+Description: Types for Glicko ratings.
+License: MIT
+-}
+
+{-# LANGUAGE DeriveGeneric #-}
+
+module BattlePlace.Skill.Glicko.Types
+	( Skill(..)
+	, ratingFromSkill
+	) where
+
+import qualified Data.Aeson as J
+import Data.Default
+import GHC.Generics(Generic)
+
+import BattlePlace.Rating
+import BattlePlace.Util
+
+data Skill = Skill
+	{ skill_rating :: {-# UNPACK #-} !Double
+	, skill_deviation :: {-# UNPACK #-} !Double
+	, skill_volatility :: {-# UNPACK #-} !Double
+	} deriving Generic
+
+-- default skill
+instance Default (Skill) where
+	def = Skill
+		{ skill_rating = 1500
+		, skill_deviation = 350
+		, skill_volatility = 0.06
+		}
+
+instance J.FromJSON (Skill) where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON (Skill) where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Conservative rating for skill.
+ratingFromSkill :: Skill -> Rating
+ratingFromSkill Skill
+	{ skill_rating = rating
+	, skill_deviation = deviation
+	} = Rating $ rating - deviation * 2
diff --git a/BattlePlace/Token/Types.hs b/BattlePlace/Token/Types.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/Token/Types.hs
@@ -0,0 +1,24 @@
+{-|
+Module: BattlePlace.Token.Types
+Description: Token types.
+License: MIT
+-}
+
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module BattlePlace.Token.Types
+	( InternalToken(..)
+	, Ticket(..)
+	) where
+
+import qualified Data.Aeson as J
+import qualified Data.Text as T
+import Servant.API
+
+-- | Internal token.
+-- Token is encrypted, and contains some internal information.
+newtype InternalToken a = InternalToken T.Text deriving (J.FromJSON, J.ToJSON, FromHttpApiData, ToHttpApiData)
+
+-- | Ticket.
+-- Ticket doesn't contain information, it's just an ID.
+newtype Ticket = Ticket T.Text deriving (J.FromJSON, J.ToJSON)
diff --git a/BattlePlace/Util.hs b/BattlePlace/Util.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/Util.hs
@@ -0,0 +1,35 @@
+{-|
+Module: BattlePlace.Util
+Description: General utilities.
+License: MIT
+-}
+
+{-# LANGUAGE LambdaCase #-}
+
+module BattlePlace.Util
+	( jsonOptions
+	, jsonOptionsWithTag
+	) where
+
+import qualified Data.Aeson as J
+
+jsonOptions :: J.Options
+jsonOptions = jsonOptionsWithTag "status"
+
+jsonOptionsWithTag :: String -> J.Options
+jsonOptionsWithTag tag = J.defaultOptions
+	{ J.fieldLabelModifier = dropBeforeUnderscore
+	, J.constructorTagModifier = dropBeforeUnderscore
+	, J.sumEncoding = J.TaggedObject
+		{ J.tagFieldName = tag
+		, J.contentsFieldName = "contents"
+		}
+	}
+
+dropBeforeUnderscore :: String -> String
+dropBeforeUnderscore = \case
+	x : xs -> case x of
+		'_' -> xs
+		_ -> dropBeforeUnderscore xs
+	[] -> []
+
diff --git a/BattlePlace/WebApi.hs b/BattlePlace/WebApi.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/WebApi.hs
@@ -0,0 +1,226 @@
+{-|
+Module: BattlePlace.WebApi
+Description: Web API definitions.
+License: MIT
+-}
+
+{-# LANGUAGE DataKinds, DeriveGeneric, GeneralizedNewtypeDeriving, TypeOperators #-}
+
+module BattlePlace.WebApi
+	( WebApi
+	, ClientAuthRequest(..)
+	, ClientAuthResponse(..)
+	, MatchRequest(..)
+	, MatchResponse(..)
+	, MatchStatusResponse(..)
+	, MatchStatusReason(..)
+	, MatchTeam(..)
+	, MatchPlayer(..)
+	, MatchServer(..)
+	, MatchServerInfo(..)
+	, SessionResultRequest(..)
+	, ServerMatchRequest(..)
+	, ServerMatchResponse(..)
+	, ServerSessionResultRequest(..)
+	, ServerSession(..)
+	) where
+
+import qualified Data.Aeson as J
+import qualified Data.Text as T
+import qualified Data.Vector as V
+import qualified Data.Vector.Unboxed as VU
+import GHC.Generics(Generic)
+import Servant.API
+
+import BattlePlace.Token.Types
+import BattlePlace.Util
+import BattlePlace.WebApi.Types
+
+type WebApi = "v1a" :>
+	(    "client" :>
+		(    "auth" :> ReqBody '[JSON] ClientAuthRequest :> Post '[JSON] ClientAuthResponse
+		:<|> "match" :>
+			(    ReqBody '[JSON] MatchRequest :> Post '[JSON] MatchResponse
+			:<|> Capture "matchToken" (InternalToken MatchToken) :> Get '[JSON] MatchStatusResponse
+			)
+		:<|> "session" :> Capture "sessionToken" (InternalToken SessionToken) :>
+			(    "result" :> ReqBody '[JSON] SessionResultRequest :> Post '[JSON] ()
+			)
+		)
+	:<|> "server" :>
+		(    "match" :> ReqBody '[JSON] ServerMatchRequest :> Post '[JSON] ServerMatchResponse
+		:<|> "session" :> Capture "serverSessionToken" (InternalToken ServerSessionToken) :>
+			(    "result" :> ReqBody '[JSON] ServerSessionResultRequest :> Post '[JSON] ()
+			)
+		)
+	)
+
+data ClientAuthRequest = ClientAuthRequest
+	{ clientAuthRequest_projectId :: {-# UNPACK #-} !ProjectId
+	, clientAuthRequest_auth :: !Auth
+	} deriving Generic
+instance J.FromJSON ClientAuthRequest where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ClientAuthRequest where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data ClientAuthResponse
+	= ClientAuthResponse_authenticated
+		{ clientAuthResponse_clientToken :: !(InternalToken ClientToken)
+		, clientAuthResponse_name :: !T.Text
+		, clientAuthResponse_pictureUrl :: !T.Text
+		}
+	| ClientAuthResponse_notAuthenticated
+		{ clientAuthResponse_error :: !T.Text
+		}
+	deriving Generic
+instance J.FromJSON ClientAuthResponse where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ClientAuthResponse where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data MatchRequest = MatchRequest
+	{ matchRequest_clientToken :: !(InternalToken ClientToken)
+	, matchRequest_teamSizes :: !(VU.Vector MatchTeamSize)
+	, matchRequest_maxMatchTime :: {-# UNPACK #-} !Int
+	, matchRequest_matchTag :: !(Maybe MatchTag)
+	, matchRequest_info :: !(Maybe MatchPlayerInfo)
+	} deriving Generic
+instance J.FromJSON MatchRequest where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchRequest where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data MatchResponse = MatchResponse
+	{ matchResponse_matchToken :: !(InternalToken MatchToken)
+	} deriving Generic
+instance J.FromJSON MatchResponse where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchResponse where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data MatchStatusResponse
+	= MatchStatusResponse_notFound
+	| MatchStatusResponse_inProgress
+	| MatchStatusResponse_matched
+		{ matchStatusResponse_sessionId :: !SessionId
+		, matchStatusResponse_sessionToken :: !(InternalToken SessionToken)
+		, matchStatusResponse_teams :: !(V.Vector MatchTeam)
+		, matchStatusResponse_teamIndex :: {-# UNPACK #-} !Int
+		, matchStatusResponse_mateIndex :: {-# UNPACK #-} !Int
+		, matchStatusResponse_server :: !(Maybe MatchServer)
+		}
+	| MatchStatusResponse_failed
+		{ matchStatusResponse_reason :: !MatchStatusReason
+		}
+	-- | Match status was cleaned.
+	-- Normallly this status should not be visible to clients, it's here just in case.
+	| MatchStatusResponse_cleaned
+	deriving Generic
+instance J.FromJSON MatchStatusResponse where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchStatusResponse where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Reason of match failure.
+data MatchStatusReason
+	-- | Failed to make a match in a specified time.
+	= MatchStatusReason_timedOut
+	-- | Match was made, but no server is available (and use of server is mandatory).
+	| MatchStatusReason_noServer
+	-- | Matching was explicitly cancelled by user.
+	| MatchStatusReason_cancelled
+	deriving Generic
+instance J.FromJSON MatchStatusReason where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchStatusReason where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+newtype MatchTeam = MatchTeam (V.Vector MatchPlayer) deriving (J.FromJSON, J.ToJSON)
+
+data MatchPlayer = MatchPlayer
+	{ matchPlayer_info :: !MatchPlayerInfo
+	, matchPlayer_ourTicket :: !(Maybe Ticket)
+	, matchPlayer_theirTicket :: !(Maybe Ticket)
+	} deriving Generic
+instance J.FromJSON MatchPlayer where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchPlayer where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data MatchServer = MatchServer
+	{ matchServer_info :: !MatchServerInfo
+	, matchServer_ourTicket :: !Ticket
+	, matchServer_theirTicket :: !Ticket
+	} deriving Generic
+instance J.FromJSON MatchServer where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchServer where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+newtype MatchServerInfo = MatchServerInfo T.Text deriving (J.FromJSON, J.ToJSON)
+
+data SessionResultRequest
+	= SessionResultRequest_finished
+		{ sessionResultRequest_ranks :: !(V.Vector Int)
+		}
+	| SessionResultRequest_cancelled
+	deriving Generic
+instance J.FromJSON SessionResultRequest where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON SessionResultRequest where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data ServerMatchRequest = ServerMatchRequest
+	{ serverMatchRequest_projectId :: {-# UNPACK #-} !ProjectId
+	, serverMatchRequest_projectServerToken :: !ProjectServerToken
+	, serverMatchRequest_maxSessionsCount :: {-# UNPACK #-} !Int
+	, serverMatchRequest_info :: !MatchServerInfo
+	, serverMatchRequest_timeout :: !(Maybe Int)
+	} deriving Generic
+instance J.FromJSON ServerMatchRequest where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ServerMatchRequest where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data ServerMatchResponse = ServerMatchResponse
+	{ serverMatchResponse_sessions :: !(V.Vector ServerSession)
+	} deriving Generic
+instance J.FromJSON ServerMatchResponse where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ServerMatchResponse where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data ServerSessionResultRequest
+	= ServerSessionResultRequest_finished
+		{ serverSessionResultRequest_ranks :: !(V.Vector Int)
+		}
+	| ServerSessionResultRequest_cancelled
+	deriving Generic
+instance J.FromJSON ServerSessionResultRequest where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ServerSessionResultRequest where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+data ServerSession = ServerSession
+	{ serverSession_sessionId :: !SessionId
+	, serverSession_serverSessionToken :: !(InternalToken ServerSessionToken)
+	, serverSession_teams :: !(V.Vector MatchTeam)
+	} deriving Generic
+instance J.FromJSON ServerSession where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ServerSession where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
diff --git a/BattlePlace/WebApi/Types.hs b/BattlePlace/WebApi/Types.hs
new file mode 100644
--- /dev/null
+++ b/BattlePlace/WebApi/Types.hs
@@ -0,0 +1,243 @@
+{-|
+Module: BattlePlace.WebApi.Types
+Description: Web API types.
+License: MIT
+-}
+
+{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, LambdaCase, ViewPatterns #-}
+
+module BattlePlace.WebApi.Types
+	( AccountId(..)
+	, ProjectId(..)
+	, Auth(..)
+	, AuthType(..)
+	, authTypeOf
+	, Client(..)
+	, ClientToken(..)
+	, ProjectServerId(..)
+	, ProjectServerToken(..)
+	, MatchTeamSize
+	, MatchTag(..)
+	, MatchPlayerInfo(..)
+	, MatchToken(..)
+	, SessionToken(..)
+	, SessionId(..)
+	, ServerSessionToken(..)
+	, Identified(..)
+	, Base64ByteString(..)
+	, Base64Word64(..)
+	, StrWord64(..)
+	) where
+
+import Control.Monad
+import qualified Data.Aeson as J
+import qualified Data.Aeson.Types as J
+import qualified Data.ByteArray as BA
+import qualified Data.ByteArray.Encoding as BA
+import qualified Data.ByteString as B
+import Data.Either
+import Data.Hashable
+import qualified Data.Serialize as S
+import Data.String
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import Data.Word
+import Foreign.Storable
+import GHC.Generics(Generic)
+import Servant.API
+
+import BattlePlace.Util
+
+-- | Account id.
+-- At the moment it's just itch user id, but that may change.
+newtype AccountId = AccountId Base64Word64 deriving (J.FromJSON, J.ToJSON, FromHttpApiData)
+
+-- | Project id.
+newtype ProjectId = ProjectId Base64Word64 deriving (Eq, Hashable, J.FromJSON, J.ToJSON, FromHttpApiData)
+
+data Auth
+	= Auth_itchJwtToken
+		{ auth_itchJwtToken :: !T.Text
+		}
+	| Auth_steamEncryptedTicket
+		{ auth_steamEncryptedTicket :: !T.Text
+		}
+	| Auth_testKey
+		{ auth_testKey :: !T.Text
+		, auth_testId :: !StrWord64
+		}
+	deriving Generic
+instance J.FromJSON Auth where
+	parseJSON = J.genericParseJSON jsonOptions
+		{ J.sumEncoding = J.UntaggedValue
+		}
+instance J.ToJSON Auth where
+	toJSON = J.genericToJSON jsonOptions
+		{ J.sumEncoding = J.UntaggedValue
+		}
+	toEncoding = J.genericToEncoding jsonOptions
+		{ J.sumEncoding = J.UntaggedValue
+		}
+
+-- | Auth type (for logging).
+data AuthType
+	= AuthType_itchJwtToken
+	| AuthType_steamEncryptedTicket
+	| AuthType_testKey
+	deriving Generic
+instance J.FromJSON AuthType where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON AuthType where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+authTypeOf :: Auth -> AuthType
+authTypeOf = \case
+	Auth_itchJwtToken {} -> AuthType_itchJwtToken
+	Auth_steamEncryptedTicket {} -> AuthType_steamEncryptedTicket
+	Auth_testKey {} -> AuthType_testKey
+
+data Client
+	= Client_itch
+		{ client_itchUserId :: {-# UNPACK #-} !StrWord64
+		}
+	| Client_steam
+		{ client_steamId :: {-# UNPACK #-} !StrWord64
+		}
+	| Client_test
+		{ client_testId :: {-# UNPACK #-} !StrWord64
+		}
+	deriving (Eq, Generic)
+instance Hashable Client
+instance J.FromJSON Client where
+	parseJSON = J.genericParseJSON $ jsonOptionsWithTag "type"
+instance J.ToJSON Client where
+	toJSON = J.genericToJSON $ jsonOptionsWithTag "type"
+	toEncoding = J.genericToEncoding $ jsonOptionsWithTag "type"
+
+data ClientToken = ClientToken
+	{ clientToken_projectId :: {-# UNPACK #-} !ProjectId
+	, clientToken_client :: !Client
+	} deriving Generic
+instance J.FromJSON ClientToken where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ClientToken where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Project's server id.
+newtype ProjectServerId = ProjectServerId Base64Word64 deriving (Eq, Hashable, J.FromJSON, J.FromJSONKey, J.ToJSON, J.ToJSONKey)
+
+-- | Project's secret server token.
+newtype ProjectServerToken = ProjectServerToken T.Text deriving (Eq, Hashable, J.FromJSON, J.ToJSON)
+
+-- | Size of a team in match request.
+type MatchTeamSize = Int
+
+-- | Match tag in match request.
+newtype MatchTag = MatchTag T.Text deriving (Eq, Hashable, Monoid, J.FromJSON, J.ToJSON)
+
+-- | Opaque player info.
+newtype MatchPlayerInfo = MatchPlayerInfo T.Text deriving (Monoid, J.FromJSON, J.ToJSON)
+
+-- | Match token.
+data MatchToken = MatchToken
+	{ matchToken_projectId :: {-# UNPACK #-} !ProjectId
+	, matchToken_client :: !Client
+	} deriving Generic
+instance J.FromJSON MatchToken where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON MatchToken where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Session token.
+data SessionToken = SessionToken
+	{ sessionToken_sessionId :: !SessionId
+	, sessionToken_teamIndex :: {-# UNPACK #-} !Int
+	, sessionToken_mateIndex :: {-# UNPACK #-} !Int
+	, sessionToken_client :: !Client
+	} deriving Generic
+instance J.FromJSON SessionToken where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON SessionToken where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Session id.
+newtype SessionId = SessionId Base64ByteString deriving (Eq, Hashable, J.FromJSON, J.ToJSON)
+
+-- | Server session token.
+newtype ServerSessionToken = ServerSessionToken
+	{ serverSessionToken_sessionId :: SessionId
+	} deriving Generic
+instance J.FromJSON ServerSessionToken where
+	parseJSON = J.genericParseJSON jsonOptions
+instance J.ToJSON ServerSessionToken where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Generic data type for id + object.
+data Identified i a = Identified
+	{ identified_id :: !i
+	, identified_info :: !a
+	} deriving Generic
+instance (J.FromJSON i, J.FromJSON a) => J.FromJSON (Identified i a) where
+	parseJSON = J.genericParseJSON jsonOptions
+instance (J.ToJSON i, J.ToJSON a) => J.ToJSON (Identified i a) where
+	toJSON = J.genericToJSON jsonOptions
+	toEncoding = J.genericToEncoding jsonOptions
+
+-- | Implementation for parseJson using base64-serialized string.
+base64ParseJsonStr :: S.Serialize a => T.Text -> J.Parser a
+base64ParseJsonStr = \case
+	(BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8 -> Right (S.decode -> Right object)) -> return object
+	_ -> fail "wrong base64 object"
+
+-- | Implementation for toJson using base64-serialized string.
+base64ToJsonStr :: S.Serialize a => a -> T.Text
+base64ToJsonStr = T.decodeUtf8 . BA.convertToBase BA.Base64URLUnpadded . S.encode
+
+-- | Implementation for parseUrlPiece using base64-serialized string.
+base64ParseUrlPiece :: S.Serialize a => T.Text -> Either T.Text a
+base64ParseUrlPiece = either (Left . T.pack) (either (Left . T.pack) Right . S.decode) . BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8
+
+-- | ByteString which serializes to JSON as base64 string.
+newtype Base64ByteString = Base64ByteString B.ByteString deriving (Eq, Ord, Monoid, Hashable, BA.ByteArray, BA.ByteArrayAccess)
+instance J.FromJSON Base64ByteString where
+	parseJSON = either fail return . BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8 <=< J.parseJSON
+instance J.ToJSON Base64ByteString where
+	toJSON = J.toJSON . T.decodeUtf8 . BA.convertToBase BA.Base64URLUnpadded
+
+-- | Word64 which serializes to JSON as base64 string.
+-- Useful because 64-bit integer is not representable in javascript.
+newtype Base64Word64 = Base64Word64 Word64 deriving (Eq, Ord, Num, Storable, Hashable, S.Serialize, Read, Show)
+instance J.FromJSON Base64Word64 where
+	parseJSON = base64ParseJsonStr <=< J.parseJSON
+instance J.FromJSONKey Base64Word64 where
+	fromJSONKey = J.FromJSONKeyTextParser base64ParseJsonStr
+instance J.ToJSON Base64Word64 where
+	toJSON = J.toJSON . base64ToJsonStr
+	toEncoding = J.toEncoding . base64ToJsonStr
+instance J.ToJSONKey Base64Word64 where
+	toJSONKey = J.toJSONKeyText base64ToJsonStr
+instance FromHttpApiData Base64Word64 where
+	parseUrlPiece = base64ParseUrlPiece
+instance IsString Base64Word64 where
+	fromString = fromRight (error "wrong base64 word") . J.parseEither J.parseJSON . J.String . T.pack
+
+-- | Word64 which serializes to JSON as decimal string.
+-- Useful because 64-bit integer is not representable in javascript.
+newtype StrWord64 = StrWord64 Word64 deriving (Eq, Ord, Num, Storable, Hashable, S.Serialize, Read, Show)
+instance J.FromJSON StrWord64 where
+	parseJSON = f . reads <=< J.parseJSON where
+		f = \case
+			[(n, "")] -> return $ StrWord64 n
+			_ -> fail "wrong number"
+instance J.ToJSON StrWord64 where
+	toJSON (StrWord64 n) = J.toJSON $ show n
+instance FromHttpApiData StrWord64 where
+	parseUrlPiece = f . reads <=< parseUrlPiece where
+		f = \case
+			[(n, "")] -> return $ StrWord64 n
+			_ -> fail "wrong number"
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-2018 Alexander Bich (aka Quyse Lert)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/battleplace.cabal b/battleplace.cabal
new file mode 100644
--- /dev/null
+++ b/battleplace.cabal
@@ -0,0 +1,35 @@
+name:                battleplace
+version:             0.1.0.0
+synopsis:            Core definitions for BattlePlace.io service
+description:         Core definitions for BattlePlace.io service
+license:             MIT
+license-file:        LICENSE
+author:              Alexander Bich <quyse0@gmail.com>
+maintainer:          Alexander Bich <quyse0@gmail.com>
+copyright:           (c) 2017-2018 Alexander Bich
+category:            Game
+stability:           experimental
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:
+    BattlePlace.Util
+    BattlePlace.Rating
+    BattlePlace.Skill.Glicko.Types
+    BattlePlace.Token.Types
+    BattlePlace.WebApi
+    BattlePlace.WebApi.Types
+  other-modules:
+  build-depends:
+    aeson
+    , base >= 4.5 && < 5
+    , bytestring
+    , cereal
+    , data-default
+    , hashable
+    , memory
+    , servant
+    , text
+    , vector
+  default-language:    Haskell2010
