diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013, Jan Ahrens
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the <organization> nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/demos/cli-demo.hs b/demos/cli-demo.hs
new file mode 100644
--- /dev/null
+++ b/demos/cli-demo.hs
@@ -0,0 +1,92 @@
+-- This demo shows how to authenticate users using OAuth and how to
+-- reuse the access tokens.
+-- It used the OAuth out of band (OOB) mode and can be used as a starting
+-- point to write an application that does not to have a web view.
+--
+-- Make sure to set the environment variables XING_CONSUMER_KEY and
+-- XING_CONSUMER_SECRET before trying this demo.
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main where
+
+import System.IO (hFlush, stdout)
+import Data.Maybe (fromJust, isJust)
+
+import Control.Monad.Trans.Control (MonadBaseControl)
+import Control.Monad.Trans.Resource (MonadResource)
+
+import Data.Monoid (mappend)
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.Text.IO as T
+
+import Web.XING
+
+import System.Environment (getEnv)
+import qualified System.IO.Error
+import qualified Control.Exception as E
+
+readVerifier
+  :: URL
+  -> IO Verifier
+readVerifier url = do
+  putStrLn "Please confirm the authorization at"
+  putStrLn $ "  " `mappend` (BS.unpack url)
+  putStr   "Please enter the PIN: " >> hFlush stdout
+  BS.getLine
+
+showAccessToken
+  :: (AccessToken, BS.ByteString)
+  -> IO ()
+showAccessToken (accessToken, uid) = do
+  putStrLn $ ""
+  putStrLn $ "Hello " `mappend` (BS.unpack uid) `mappend` "!"
+  putStrLn $ "You should set the access token as environment variables:"
+  putStrLn $ ""
+  putStrLn $ "  export XING_ACCESS_TOKEN_KEY=\""    `mappend` ((BS.unpack.token)       accessToken) `mappend` "\""
+  putStrLn $ "  export XING_ACCESS_TOKEN_SECRET=\"" `mappend` ((BS.unpack.tokenSecret) accessToken) `mappend` "\""
+  putStrLn $ ""
+
+handshake
+  :: (MonadResource m, MonadBaseControl IO m)
+  => OAuth
+  -> Manager
+  -> m (AccessToken, BS.ByteString)
+handshake oa manager = do
+  (requestToken, url) <- getRequestToken oa manager
+  verifier <- liftIO $ readVerifier url
+  getAccessToken requestToken verifier oa manager
+
+readAccessToken :: IO (Maybe Credential)
+readAccessToken = do
+  access_token_key    <- getEnv "XING_ACCESS_TOKEN_KEY"
+  access_token_secret <- getEnv "XING_ACCESS_TOKEN_SECRET"
+  return $ Just $ newCredential (BS.pack access_token_key) (BS.pack access_token_secret)
+
+main :: IO ()
+main = do
+  consumer_key    <- getEnv "XING_CONSUMER_KEY"
+  consumer_secret <- getEnv "XING_CONSUMER_SECRET"
+  let xingConsumer = consumer (BS.pack consumer_key) (BS.pack consumer_secret)
+
+  maybeAccessToken <- E.catch (readAccessToken) (\(_ :: System.IO.Error.IOError) -> return Nothing)
+
+  putStrLn "XING API demo"
+  user <- withManager $ \manager -> do
+    accessToken <- if isJust maybeAccessToken
+      then return $ fromJust maybeAccessToken
+      else auth xingConsumer manager
+    getIdCard xingConsumer manager accessToken
+  T.putStrLn (displayName user)
+
+auth
+  :: (MonadResource m, MonadBaseControl IO m)
+  => OAuth
+  -> Manager
+  -> m RequestToken
+auth oa manager = do
+  res@(accessToken, _) <- handshake oa manager
+  liftIO $ showAccessToken res
+  return accessToken
diff --git a/demos/minimal-demo.hs b/demos/minimal-demo.hs
new file mode 100644
--- /dev/null
+++ b/demos/minimal-demo.hs
@@ -0,0 +1,28 @@
+-- This is a minimal example that can fit in the README and shows how to
+-- do the OAuth handshake and call a protected resource.
+-- It's not the best example since it's not reusing access tokens.
+-- Have a look at the cli-demo.hs file for a more complex example.
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Web.XING
+import qualified Data.ByteString.Char8 as BS
+
+oauthConfig :: OAuth
+oauthConfig = consumer "YOUR_CONSUMER_KEY" "YOUR_CONSUMER_SECRET"
+
+main :: IO ()
+main = withManager $ \manager -> do
+  (accessToken, _) <- handshake manager
+  idCard <- getIdCard oauthConfig manager accessToken
+  liftIO $ putStrLn $ show idCard
+  where
+    handshake manager = do
+      (requestToken, url) <- getRequestToken oauthConfig manager
+      verifier <- liftIO $ do
+        BS.putStrLn url
+        BS.putStr "PIN: "
+        BS.getLine
+      getAccessToken requestToken verifier oauthConfig manager
diff --git a/demos/yesod-demo.hs b/demos/yesod-demo.hs
new file mode 100644
--- /dev/null
+++ b/demos/yesod-demo.hs
@@ -0,0 +1,200 @@
+-- This demo shows how to use Yesod with the XING API.
+--
+-- The OAuth handshake is implemented without using yesod-auth.
+-- Have a look at the yesod-auth-xing package for a simpler
+-- solution to authenticate users using the XING API.
+--
+-- Make sure to set the environment variables XING_CONSUMER_KEY and
+-- XING_CONSUMER_SECRET before trying this demo.
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies #-}
+
+module Main where
+
+-- TODO: Web.XING.Types.User.FullUser also exports languages. Find a better name.
+import           Yesod.Core hiding (languages)
+import           Network.Wai.Handler.Warp (run)
+import           Text.Hamlet (hamlet)
+import           Web.XING
+import           Network.HTTP.Conduit (newManager, def)
+import           Data.Maybe (fromJust, isJust, fromMaybe)
+import qualified Data.Map as M
+import           Helper.YesodHelper ( bootstrapLayout, bootstrapCDN
+                                    , writeTokenToSession, getTokenFromSession
+                                    , deleteTokenFromSession )
+import qualified Data.ByteString.Char8 as BS
+import           Data.Monoid (mappend)
+import qualified Data.Text.Encoding as E
+import           Data.Time
+import qualified Data.Text as T
+import           System.Environment (getEnv)
+
+data HelloXING = HelloXING {
+    httpManager   :: Manager
+  , oAuthConsumer :: OAuth
+}
+
+instance Yesod HelloXING where
+  defaultLayout = bootstrapLayout
+
+mkYesod "HelloXING" [parseRoutes|
+  /          HomeR      GET
+  /handshake HandshakeR POST
+  /callback  CallbackR  GET
+  /logout    LogoutR    POST
+|]
+
+postHandshakeR :: Handler RepHtml
+postHandshakeR = do
+  yesod <- getYesod
+  let oa = oAuthConsumer yesod
+  let manager = httpManager yesod
+
+  (requestToken, url) <- getRequestToken oa manager
+  writeTokenToSession "request" requestToken
+
+  redirect $ E.decodeUtf8 url
+
+getCallbackR :: Handler RepHtml
+getCallbackR = do
+  yesod <- getYesod
+  let oa = oAuthConsumer yesod
+  let manager = httpManager yesod
+
+  maybeRequestToken <- getTokenFromSession "request"
+
+  maybeVerifier <- lookupGetParam "oauth_verifier"
+  let verifier = E.encodeUtf8 (fromMaybe "" maybeVerifier)
+
+  if isJust maybeRequestToken
+    then do
+      (accessToken, _) <- getAccessToken (fromJust maybeRequestToken) verifier oa manager
+      writeTokenToSession "access" accessToken
+    else return ()
+
+  redirect HomeR
+
+postLogoutR :: Handler RepHtml
+postLogoutR = do
+  deleteTokenFromSession "access"
+  deleteTokenFromSession "request"
+  redirect HomeR
+
+daysUntilBirthday
+  :: Maybe BirthDate
+  -> Day
+  -> Integer
+daysUntilBirthday (Just (DayOnly birthMonth birthDay)) today    = calcDays birthMonth birthDay today
+daysUntilBirthday (Just (FullDate _ birthMonth birthDay)) today = calcDays birthMonth birthDay today
+daysUntilBirthday Nothing _                                     = 0
+
+calcDays
+  :: Int
+  -> Int
+  -> Day
+  -> Integer
+calcDays birthMonth birthDay today
+  = if (birthDayThisYear >= 0)
+      then birthDayThisYear
+      else birthDayNextYear
+  where
+    (year, _, _)     = toGregorian today
+    birthDayThisYear = diffDays (fromGregorian (year    ) birthMonth birthDay) today
+    birthDayNextYear = diffDays (fromGregorian (year + 1) birthMonth birthDay) today
+
+getHomeR :: Handler RepHtml
+getHomeR = do
+  maybeAccessToken <- getTokenFromSession "access"
+
+  widget <- case maybeAccessToken of
+    Just accessToken -> do
+      yesod <- getYesod
+      users <- getUsers (oAuthConsumer yesod) (httpManager yesod) accessToken ["me"]
+      today <- liftIO $ getCurrentTime
+      let firstUser = head $ unUserList users
+      let birthDayInDays = daysUntilBirthday (birthDate firstUser) (utctDay today)
+      return $ whoAmI firstUser birthDayInDays
+
+    Nothing -> return pleaseLogIn
+
+  defaultLayout $ do
+    addStylesheetRemote $ bootstrapCDN `mappend` "/css/bootstrap-combined.min.css"
+    [whamlet|
+      <h1>Welcome to the XING API demo
+      ^{widget}
+    |]
+
+pleaseLogIn :: Widget
+pleaseLogIn =
+  toWidget [hamlet|
+    <img src="https://www.xing.com/img/n/nobody_m.png">
+    <p>Hello unknown user. Please log-in.
+    <form method=POST action=@{HandshakeR}>
+      <input type=submit value="Login with XING">
+  |]
+
+whoAmI
+  :: FullUser
+  -> Integer
+  -> Widget
+whoAmI user birthDayInDays = do
+  toWidget [hamlet|
+    <img src=#{fromMaybe "" $ M.lookup "large" (photoUrls user)}>
+    <p>
+      <a href=#{permalink user}>#{displayName user}
+    <p>
+      Hey #{firstName user}! Welcome to this demo.<br>
+
+
+    <p>
+      Your next birthday is in #{show birthDayInDays} days.
+
+    <p>
+      Did you know? In Germany you would be greeted with "Guten Tag 
+      $if (gender user) == Male
+        Herr
+      $else
+        Frau
+      \ #{lastName user}".
+
+    $if (length (M.keys (languages user)) > 1)
+      <p>
+        Impressive! You are speaking #{length (M.keys (languages user))} languages:
+        \  #{T.intercalate ", " (M.keys (languages user))}.
+
+    $maybe mail <- activeEmail user
+      <p>Your active email address is <a href=mailto:#{mail}>#{mail}</a>.
+
+    <p>Here is a list of your premium services
+    <ul>
+      $forall service <- premiumServices user
+        <li>#{service}
+
+    $if null $ badges user
+      You have no badges.
+    $else
+      <p>Your badges:
+      <ul>
+        $forall badge <- badges user
+          <li>#{badge}
+
+    <form method=POST action=@{LogoutR}>
+      <input type=submit value="Logout">
+  |]
+
+main :: IO ()
+main = do
+  manager <- newManager def
+  let port = 3000
+  consumer_key    <- getEnv "XING_CONSUMER_KEY"
+  consumer_secret <- getEnv "XING_CONSUMER_SECRET"
+  let xingConsumer = consumer (BS.pack consumer_key) (BS.pack consumer_secret)
+  let xingConsumer' = xingConsumer{
+    oauthCallback = Just $ "http://localhost:" `mappend` (BS.pack.show) port `mappend` "/callback"
+  }
+  putStrLn $ "Starting on port " ++ show port
+  run port =<< toWaiApp (HelloXING manager xingConsumer')
diff --git a/lib/Web/XING.hs b/lib/Web/XING.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING.hs
@@ -0,0 +1,21 @@
+module Web.XING
+    (
+      module Web.XING.Auth
+    , module Web.XING.API
+    , module Web.XING.Types
+    -- * Calls
+    , module Web.XING.Calls.IdCard
+    , module Web.XING.Calls.User
+    -- * Common used functions (re-exports)
+    , liftIO
+    , withManager
+    ) where
+
+import Web.XING.Auth
+import Web.XING.API
+import Web.XING.Types
+import Web.XING.Calls.IdCard
+import Web.XING.Calls.User
+
+import Control.Monad.IO.Class (MonadIO (liftIO))
+import Network.HTTP.Conduit (withManager)
diff --git a/lib/Web/XING/API.hs b/lib/Web/XING/API.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/API.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Web.XING.API
+    ( -- * API request interface
+      apiRequest
+    ) where
+
+import           Network.HTTP.Types (Method)
+import           Network.HTTP.Conduit
+                   (Response(..), Request(..), parseUrl, httpLbs)
+import           Data.Maybe (fromJust)
+import           Data.Monoid (mappend)
+import qualified Data.ByteString.Char8      as BS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           Control.Monad.Trans.Control (MonadBaseControl)
+import           Control.Monad.Trans.Resource (MonadResource)
+import           Web.XING.Types
+import           Web.XING.API.Error
+import qualified Control.Exception.Lifted as E
+import           Web.Authenticate.OAuth (signOAuth)
+
+apiBaseUrl :: BS.ByteString
+apiBaseUrl = "https://api.xing.com"
+
+-- | Low level API request interface
+apiRequest
+  :: (MonadResource m, MonadBaseControl IO m)
+  => OAuth         -- ^ OAuth consumer
+  -> Manager
+  -> AccessToken
+  -> Method        -- ^ HTTP verb
+  -> BS.ByteString -- ^ HTTP path
+  -> m (Response BSL.ByteString)
+apiRequest oa manager cr m uri  = do
+  let req = fromJust $ parseUrl $ BS.unpack (apiBaseUrl `mappend` uri)
+  req' <- signOAuth oa cr req{method = m}
+  E.catch (httpLbs req' manager)
+    (handleStatusCodeException (m `mappend` " " `mappend` uri))
diff --git a/lib/Web/XING/API/Error.hs b/lib/Web/XING/API/Error.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/API/Error.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Web.XING.API.Error
+    (
+      mapError
+    , handleError
+    , handleStatusCodeException
+    ) where
+
+import Web.XING.Types
+import Network.HTTP.Types (ResponseHeaders)
+import Data.Maybe (fromMaybe)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.ByteString.Char8 as BS
+import Control.Applicative ((<$>), (<*>))
+import Data.Aeson (decode, FromJSON(parseJSON), Value(Object) , (.:))
+import Control.Exception (throw)
+import Network.HTTP.Conduit (HttpException(StatusCodeException))
+
+mapError
+  :: XINGError
+  -> APIError
+mapError (XINGError "INVALID_OAUTH_SIGNATURE" _)        = OAuthError "Invalid oauth signature. Check your consumer and access token secret."
+mapError (XINGError "INVALID_OAUTH_CONSUMER" _)         = OAuthError "Invalid oauth consumer key. Check your config."
+mapError (XINGError "INSUFFICIENT_PRIVILEGES" _)        = OAuthError "Required permission missing"
+mapError (XINGError "INVALID_OAUTH_VERSION" _)          = OAuthError "Invalid oauth version"
+mapError (XINGError "INVALID_OAUTH_SIGNATURE_METHOD" _) = OAuthError "Invalid oauth version"
+mapError (XINGError "REQUIRED_PARAMETER_MISSING" _)     = OAuthError "Required parameter missing. Should never have happened."
+mapError (XINGError "INVALID_REQUEST_TOKEN" _)          = OAuthError "Invalid request token. Maybe the verifier was wrong?"
+mapError (XINGError "INVALID_OAUTH_TOKEN" _)            = TokenError "The oauth token was invalid. Maybe the user revoked it?"
+mapError (XINGError "TIME_EXPIRED" _)                   = OAuthError "Time was too old. Make sure that your local clock is set correctly."
+mapError (XINGError "RATE_LIMIT_EXCEEDED" _)            = Throttled
+mapError (XINGError "CALLBACK_URL_NOT_ALLOWED" _)       = CallError "The callback URL was not allowed" -- TOOD API shows better error message
+mapError _                                              = CallError "unknown"
+
+data XINGError =
+  XINGError BSL.ByteString BSL.ByteString
+  deriving (Show, Eq)
+
+instance FromJSON XINGError where
+  parseJSON (Object response) =
+    XINGError <$> (response .: "error_name")
+              <*> (response .: "message")
+  parseJSON _ = fail "no parse"
+
+handleStatusCodeException
+  :: BS.ByteString
+  -> HttpException
+  -> a
+handleStatusCodeException call (StatusCodeException status headers) = throw $ handleError status headers call
+handleStatusCodeException _    e                                    = throw e
+
+handleError
+  :: Status
+  -> ResponseHeaders
+  -> BS.ByteString
+  -> APIError
+handleError _ headers _ =
+  case decode $ BSL.fromChunks [fromMaybe "{}" (lookup "X-Response-Body-Start" headers)] of
+    Just e  -> mapError e
+    Nothing -> CallError "unknown"
diff --git a/lib/Web/XING/Auth.hs b/lib/Web/XING/Auth.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Auth.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Web.XING.Auth
+    ( -- * OAuth
+      consumer
+    , getRequestToken
+    , Patch.authorizeUrl
+    , getAccessToken
+    , Patch.token
+    , Patch.tokenSecret
+      -- reexports of some Web.Authenticate.OAuth functions
+    , newCredential -- | (re)create an OAuth token (for example access token)
+    , oauthCallback -- | extract the OAuth callback from a consumer
+    ) where
+
+import Prelude
+import qualified Control.Exception.Lifted as E
+import qualified Data.ByteString as BS
+
+import Web.Authenticate.OAuth hiding (getAccessToken)
+import qualified Web.XING.Internal.AuthenticateOAuthPatch as Patch
+import Control.Monad.Trans.Control (MonadBaseControl)
+import Control.Monad.Trans.Resource (MonadResource)
+
+import Data.Maybe (fromMaybe)
+
+import Web.XING.Types
+import Web.XING.API.Error
+
+-- | Create an OAuth consumer
+consumer
+  :: BS.ByteString -- ^ consumer key
+  -> BS.ByteString -- ^ consumer secret
+  -> OAuth
+consumer consumerKey consumerSecret = newOAuth
+  { oauthRequestUri      = "https://api.xing.com/v1/request_token"
+  , oauthAccessTokenUri  = "https://api.xing.com/v1/access_token"
+  , oauthAuthorizeUri    = "https://api.xing.com/v1/authorize"
+  , oauthSignatureMethod = PLAINTEXT
+  , oauthVersion         = OAuth10a
+  , oauthConsumerKey     = consumerKey
+  , oauthConsumerSecret  = consumerSecret
+  , oauthCallback        = Just "oob"
+  }
+
+-- | Create a request token (/temporary credentials/)
+getRequestToken
+ :: (MonadResource m, MonadBaseControl IO m)
+ => OAuth -- ^ OAuth consumer
+ -> Manager
+ -> m (RequestToken, URL) -- ^ request token and authorize URL
+getRequestToken oa manager = E.catch
+    tryToGetRequestToken
+    (handleStatusCodeException "POST /v1/request_token")
+  where
+    tryToGetRequestToken = do
+      cred <- Patch.getTemporaryCredential' id oa manager
+      return (cred, Patch.authorizeUrl oa cred)
+
+-- | Exchange request token for an access token (/token credentials/)
+getAccessToken
+  :: (MonadResource m, MonadBaseControl IO m)
+  => RequestToken -- ^ request token obtained from 'getRequestToken'
+  -> Verifier     -- ^ verifier obtained by calling 'Patch.authorizeUrl'
+  -> OAuth        -- ^ OAuth consumer (see 'consumer')
+  -> Manager
+  -> m (Credential, BS.ByteString)
+getAccessToken requestToken verifier oa manager = E.catch
+    tryToExchangeRequestToken
+    (handleStatusCodeException "POST /v1/access_token")
+  where
+    tryToExchangeRequestToken = do
+      accessToken <- Patch.getAccessToken' id oa ((injectVerifier verifier) requestToken) manager
+      return (accessToken, fromMaybe "" . lookup "user_id" . unCredential $ accessToken)
diff --git a/lib/Web/XING/Calls/IdCard.hs b/lib/Web/XING/Calls/IdCard.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Calls/IdCard.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts  #-}
+
+module Web.XING.Calls.IdCard
+    (
+      getIdCard
+    , demoIdCard
+    , demoIdCard'
+    ) where
+
+import Web.XING.Types
+import Web.XING.API
+
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import Network.HTTP.Conduit (Response(..))
+import Control.Monad.Trans.Control (MonadBaseControl)
+import Control.Monad.Trans.Resource (MonadResource)
+import Control.Exception (throw)
+import Data.Aeson (encode, decode, Value(..), object, (.=))
+
+-- | Get your id card <https://dev.xing.com/docs/get/users/me/id_card>
+getIdCard
+  :: (MonadResource m, MonadBaseControl IO m)
+  => OAuth
+  -> Manager
+  -> AccessToken
+  -> m MinimalUser
+getIdCard oa manager cr = do
+  Response _ _ _ body <- apiRequest oa manager cr "GET" "/v1/users/me/id_card"
+  case decode body of
+    Just a -> return a
+    Nothing -> throw Mapping
+
+-- https://dev.xing.com/docs/get/users/me/id_card
+demoIdCard :: Value
+demoIdCard = object [
+    "id_card" .= object [
+        "id"           .= ("12345_abcdef" :: BSL.ByteString)
+      , "display_name" .= ("Max Mustermann" :: BSL.ByteString)
+      , "permalink"    .= ("https://www.xing.com/profile/Max_Mustermann" :: BSL.ByteString)
+      , "photo_urls"   .= object [
+          "large"        .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg" :: BSL.ByteString)
+        , "mini_thumb"   .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.18x24.jpg"   :: BSL.ByteString)
+        , "thumb"        .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.30x40.jpg"   :: BSL.ByteString)
+        , "medium_thumb" .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.57x75.jpg"   :: BSL.ByteString)
+        , "maxi_thumb"   .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.70x93.jpg"   :: BSL.ByteString)
+      ]
+    ]
+  ]
+
+demoIdCard' :: BSL.ByteString
+demoIdCard' = encode demoIdCard
diff --git a/lib/Web/XING/Calls/User.hs b/lib/Web/XING/Calls/User.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Calls/User.hs
@@ -0,0 +1,192 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module Web.XING.Calls.User
+    (
+      demoUser,  demoUser'
+    , demoUsers, demoUsers'
+    , getUsers
+    ) where
+
+import Web.XING.Types
+import Web.XING.API
+import Data.Aeson (encode, decode, Value(..), object, (.=))
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import Network.HTTP.Conduit (Response(..))
+import Control.Monad.Trans.Control (MonadBaseControl)
+import Control.Monad.Trans.Resource (MonadResource)
+import Data.Monoid (mappend)
+import Control.Exception (throw)
+import Data.Text.Encoding (encodeUtf8)
+import Data.Text (intercalate)
+
+-- | Get user details <https://dev.xing.com/docs/get/users/:id>
+getUsers
+  :: (MonadResource m, MonadBaseControl IO m)
+  => OAuth
+  -> Manager
+  -> AccessToken
+  -> [UserId]
+  -> m UserList
+getUsers oa manager cr uids = do
+  Response _ _ _ body <- apiRequest oa manager cr "GET" ("/v1/users/" `mappend` (encodeUtf8 $ intercalate "," uids))
+  case decode body of
+    Just a  -> return a
+    Nothing -> throw Mapping
+
+-- https://dev.xing.com/docs/get/users/:id
+demoUsers :: Value
+demoUsers = object [
+    "users" .= [demoUser]
+  ]
+
+demoUsers' :: BSL.ByteString
+demoUsers' = encode demoUsers
+
+demoUser :: Value
+demoUser = object [
+        "id"           .= ("12345_abcdef" :: BSL.ByteString)
+      , "display_name" .= ("Max Mustermann" :: BSL.ByteString)
+      , "permalink"    .= ("https://www.xing.com/profile/Max_Mustermann" :: BSL.ByteString)
+      , "first_name"   .= ("Max" :: BSL.ByteString)
+      , "last_name"    .= ("Mustermann" :: BSL.ByteString)
+      , "page_name"    .= ("Max_Mustermann" :: BSL.ByteString)
+      , "gender"       .= ("m" :: BSL.ByteString)
+      , "active_email" .= ("max.mustermann@xing.com" :: BSL.ByteString)
+      , "time_zone" .= object [
+          "name"       .= ("Europe/Copenhagen" :: BSL.ByteString)
+        , "utc_offset" .= (2.0 :: Float)
+      ]
+      , "premium_services" .= (["SEARCH", "PRIVATEMESSAGES"] :: [BSL.ByteString])
+      , "badges"    .= (["PREMIUM", "PRIVATEMESSAGES"] :: [BSL.ByteString])
+      , "languages" .= object [
+          "de" .= ("NATIVE" :: BSL.ByteString)
+        , "en" .= ("FLUENT" :: BSL.ByteString)
+        , "fr" .= Null
+        , "zh" .= ("BASIC" :: BSL.ByteString)
+      ]
+      , "wants"     .= (encodeUtf8 "einen neuen Job")
+      , "haves"     .= (encodeUtf8 "viele tolle Skills")
+      , "interests" .= (encodeUtf8 "Flitzebogen schießen and so on")
+      , "organisation_member" .= (encodeUtf8 "ACM, GI")
+      , "private_address" .= object [
+          "street"       .= (encodeUtf8 "Privatstraße 1")
+        , "zip_code"     .= (encodeUtf8 "20357")
+        , "city"         .= (encodeUtf8 "Hamburg")
+        , "province"     .= (encodeUtf8 "Hamburg")
+        , "country"      .= (encodeUtf8 "DE")
+        , "email"        .= (encodeUtf8 "max@mustermann.de")
+        , "phone"        .= (encodeUtf8 "49|40|1234560")
+        , "fax"          .= (encodeUtf8 "||")
+        , "mobile_phone" .= (encodeUtf8 "49|0155|1234567")
+      ]
+      , "business_address" .= object [
+          "city"         .= (encodeUtf8 "Hamburg")
+        , "country"      .= (encodeUtf8 "DE")
+        , "zip_code"     .= (encodeUtf8 "20357")
+        , "street"       .= (encodeUtf8 "Geschäftsstraße 1a")
+        , "phone"        .= (encodeUtf8 "49|40|1234569")
+        , "fax"          .= (encodeUtf8 "49|40|1234561")
+        , "province"     .= (encodeUtf8 "Hamburg")
+        , "email"        .= (encodeUtf8 "max.mustermann@xing.com")
+        , "mobile_phone" .= (encodeUtf8 "49|160|66666661")
+      ]
+      , "web_profiles" .= object [
+          "qype"        .= (["http://qype.de/users/foo"] :: [BSL.ByteString])
+        , "google_plus" .= (["http://plus.google.com/foo"] :: [BSL.ByteString])
+        , "blog"        .= (["http://blog.example.org"] :: [BSL.ByteString])
+        , "homepage"    .= (["http://example.org", "http://another-example.org"] :: [BSL.ByteString])
+      ]
+      , "instant_messaging_accounts" .= object [
+          "skype"      .= (encodeUtf8 "1122334455")
+        , "googletalk" .= (encodeUtf8 "max.mustermann")
+      ]
+      , "professional_experience" .= object [
+          "primary_company" .= object [
+              "name"         .= (encodeUtf8 "XING AG")
+            , "title"        .= (encodeUtf8 "Softwareentwickler")
+            , "company_size" .= (encodeUtf8 "201-500")
+            , "tag"          .= Null
+            , "url"          .= (encodeUtf8 "http://www.xing.com")
+            , "career_level" .= (encodeUtf8 "PROFESSIONAL_EXPERIENCED")
+            , "begin_date"   .= (encodeUtf8 "2010-01")
+            , "description"  .= Null
+            , "end_date"     .= Null
+            , "industry"     .= (encodeUtf8 "AEROSPACE")
+        ]
+        , "non_primary_companies" .= [
+            object [
+              "name"         .= (encodeUtf8 "Ninja Ltd.")
+            , "title"        .= (encodeUtf8 "DevOps")
+            , "company_size" .= Null
+            , "tag"          .= (encodeUtf8 "NINJA")
+            , "url"          .= (encodeUtf8 "http://www.ninja-ltd.co.uk")
+            , "career_level" .= Null
+            , "begin_date"   .= (encodeUtf8 "2009-04")
+            , "description"  .= Null
+            , "end_date"     .= (encodeUtf8 "2010-07")
+            , "industry"     .= (encodeUtf8 "ALTERNATIVE_MEDICINE")
+          ]
+          , object [
+              "name"         .= Null
+            , "title"        .= (encodeUtf8 "Wiss. Mitarbeiter")
+            , "company_size" .= Null
+            , "tag"          .= (encodeUtf8 "OFFIS")
+            , "url"          .= (encodeUtf8 "http://www.uni.de")
+            , "career_level" .= Null
+            , "begin_date"   .= (encodeUtf8 "2007")
+            , "description"  .= Null
+            , "end_date"     .= (encodeUtf8 "2008")
+            , "industry"     .= (encodeUtf8 "APPAREL_AND_FASHION")
+          ]
+          , object [
+              "name"         .= Null
+            , "title"        .= (encodeUtf8 "TEST NINJA")
+            , "company_size" .= (encodeUtf8 "201-500")
+            , "tag"          .= (encodeUtf8 "TESTCOMPANY")
+            , "url"          .= Null
+            , "career_level" .= (encodeUtf8 "ENTRY_LEVEL")
+            , "begin_date"   .= (encodeUtf8 "1998-12")
+            , "description"  .= Null
+            , "end_date"     .= (encodeUtf8 "1999-05")
+            , "industry"     .= (encodeUtf8 "ARTS_AND_CRAFTS")
+          ]
+        ]
+        , "awards" .= [
+          object [
+              "name"         .= (encodeUtf8 "Awesome Dude Of The Year")
+            , "date_awarded" .= (2007 :: Int)
+            , "url"          .= Null
+          ]
+        ]
+      ]
+      , "educational_background" .= object [
+          "schools" .= [
+            object [
+              "name"    .= (encodeUtf8 "Carl-von-Ossietzky Universtät Schellenburg")
+            , "degree"  .= (encodeUtf8 "MSc CE/CS")
+            , "notes"   .= Null
+            , "subject" .= Null
+            , "begin_date" .= (encodeUtf8 "1998-08")
+            , "end_date"   .= (encodeUtf8 "2005-02")
+            ]
+          ]
+        , "qualifications" .= (["TOEFLS", "PADI AOWD"] :: [BSL.ByteString])
+      ]
+      , "photo_urls"   .= object [
+          "large"        .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg" :: BSL.ByteString)
+        , "mini_thumb"   .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.18x24.jpg"   :: BSL.ByteString)
+        , "thumb"        .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.30x40.jpg"   :: BSL.ByteString)
+        , "medium_thumb" .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.57x75.jpg"   :: BSL.ByteString)
+        , "maxi_thumb"   .= ("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.70x93.jpg"   :: BSL.ByteString)
+      ]
+      , "birth_date"   .= object [
+          "day"   .= (12 :: Int)
+        , "month" .= (8 :: Int)
+        , "year"  .= (1963 :: Int)
+      ]
+    ]
+
+demoUser' :: BSL.ByteString
+demoUser' = encode demoUser
diff --git a/lib/Web/XING/Internal/AuthenticateOAuthPatch.hs b/lib/Web/XING/Internal/AuthenticateOAuthPatch.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Internal/AuthenticateOAuthPatch.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts  #-}
+
+module Web.XING.Internal.AuthenticateOAuthPatch
+    ( -- * changed Web.Authenticate.OAuth functions
+      getTemporaryCredential'
+    , authorizeUrl
+    , getAccessToken'
+      -- * accessor functions for Credential (from Web.Authenticate.OAuth)
+    , token
+    , tokenSecret
+    ) where
+
+import Web.Authenticate.OAuth hiding (authorizeUrl, getAccessToken', getTemporaryCredential')
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import Control.Monad.Trans.Resource (MonadResource, MonadBaseControl)
+import Control.Monad.IO.Class (liftIO)
+import Control.Exception (throwIO)
+import Network.HTTP.Conduit (Manager, Request(method), httpLbs, responseStatus, responseBody, parseUrl)
+import Network.HTTP.Types (status201, parseSimpleQuery)
+import Data.Maybe (fromJust, fromMaybe)
+
+-- | extract the token from the 'Credential'
+token :: Credential -> BS.ByteString
+token = fromMaybe "" . lookup "oauth_token" . unCredential
+
+-- | extract the secret from the 'Credential'
+tokenSecret :: Credential -> BS.ByteString
+tokenSecret = fromMaybe "" . lookup "oauth_token_secret" . unCredential
+
+toStrict :: BSL.ByteString -> BS.ByteString
+toStrict = BS.concat . BSL.toChunks
+
+-- we can't use OA.getTemporaryCredential', because the XING API returns 201 instead of 200
+getTemporaryCredential' :: (MonadResource m, MonadBaseControl IO m)
+                => (Request m -> Request m)
+                -> OAuth
+                -> Manager
+                -> m Credential
+getTemporaryCredential' hook oa manager = do
+  let req = fromJust $ parseUrl $ oauthRequestUri oa
+      crd = maybe id (insert "oauth_callback") (oauthCallback oa) $ emptyCredential
+  req' <- signOAuth oa crd $ hook (req { method = "POST" })
+  rsp <- httpLbs req' manager
+  if responseStatus rsp == status201
+    then do
+      let dic = parseSimpleQuery . toStrict . responseBody $ rsp
+      return $ Credential dic
+    else liftIO . throwIO . OAuthException $ "Gaining OAuth Temporary Credential Failed: " ++ BSL.unpack (responseBody rsp)
+
+-- | URL to obtain OAuth verifier
+authorizeUrl :: OAuth         -- ^ OAuth consumer
+             -> Credential    -- ^ request token 'Web.XING.getRequestToken'
+             -> BS.ByteString -- ^ URL to send the user to
+authorizeUrl consumer = BS.pack . (authorizeUrl' (\_ -> const []) consumer{oauthCallback=Nothing})
+
+-- we can't use OA.getAccessToken, because the XING API returns 201 instead of 200
+getAccessToken' :: (MonadResource m, MonadBaseControl IO m)
+                => (Request m -> Request m)
+                -> OAuth
+                -> Credential
+                -> Manager
+                -> m Credential
+getAccessToken' hook oa cr manager = do
+  let req = hook (fromJust $ parseUrl $ oauthAccessTokenUri oa) { method = "POST" }
+  rsp <- flip httpLbs manager =<< signOAuth oa (if oauthVersion oa == OAuth10 then delete "oauth_verifier" cr else cr) req
+  if responseStatus rsp == status201
+    then do
+      let dic = parseSimpleQuery . toStrict . responseBody $ rsp
+      return $ Credential dic
+    else liftIO . throwIO . OAuthException $ "Gaining OAuth Token Credential Failed: " ++ BSL.unpack (responseBody rsp)
diff --git a/lib/Web/XING/Types.hs b/lib/Web/XING/Types.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Web.XING.Types
+    (
+      APIError(..)
+    , RequestToken
+    , Verifier
+    , AccessToken
+    , URL
+      -- reexports
+    , Manager
+    , OAuth(..)
+    , Credential(..)
+    , Status
+    -- * XING data types
+    , module Web.XING.Types.Address
+    , module Web.XING.Types.Award
+    , module Web.XING.Types.BirthDate
+    , module Web.XING.Types.ProfessionalExperience
+    , module Web.XING.Types.User
+    , module Web.XING.Types.User.FullUser
+    , module Web.XING.Types.User.MinimalUser
+    ) where
+
+import Network.HTTP.Conduit (Manager)
+import qualified Data.ByteString as BS
+import Web.Authenticate.OAuth (OAuth(..), Credential(..))
+import Network.HTTP.Types (Status)
+import Control.Exception (Exception)
+import Data.Typeable (Typeable)
+
+import Web.XING.Types.BirthDate
+import Web.XING.Types.User
+import Web.XING.Types.Address
+import Web.XING.Types.Award
+import Web.XING.Types.ProfessionalExperience
+import Web.XING.Types.User.MinimalUser
+import Web.XING.Types.User.FullUser
+
+data APIError
+  = OAuthError String
+  | TokenError String
+  | CallError String
+  | Throttled
+  | Mapping
+  deriving (Eq, Show, Typeable)
+
+instance Exception APIError
+
+type RequestToken = Credential
+type AccessToken  = Credential
+type Verifier     = BS.ByteString
+type URL          = BS.ByteString
diff --git a/lib/Web/XING/Types/Address.hs b/lib/Web/XING/Types/Address.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/Address.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Web.XING.Types.Address
+    (
+      Address(..)
+    ) where
+
+import Data.Aeson (Value(..), FromJSON(..), (.:))
+import Control.Monad (mzero)
+import Control.Applicative ((<*>), (<$>))
+import Data.Text (Text)
+
+-- the address model is pretty terrible -  nothing is certain ..
+data Address
+  = Address {
+      street      :: Maybe Text
+    , zipCode     :: Maybe Text
+    , city        :: Maybe Text
+    , province    :: Maybe Text
+    , country     :: Maybe Text
+    , email       :: Maybe Text
+    , phone       :: Maybe Text
+    , fax         :: Maybe Text
+    , mobilePhone :: Maybe Text
+  }
+  deriving (Show, Eq)
+
+instance FromJSON Address where
+  parseJSON (Object response) = do
+    Address <$> (response .: "street")
+            <*> (response .: "zip_code")
+            <*> (response .: "city")
+            <*> (response .: "province")
+            <*> (response .: "country")
+            <*> (response .: "email")
+            <*> (response .: "phone")
+            <*> (response .: "fax")
+            <*> (response .: "mobile_phone")
+  parseJSON _ = mzero
diff --git a/lib/Web/XING/Types/Award.hs b/lib/Web/XING/Types/Award.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/Award.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Web.XING.Types.Award
+    (
+      Award(..)
+    ) where
+
+import Data.Aeson (Value(..), FromJSON(..), (.:), (.:?))
+import Control.Monad (mzero)
+import Control.Applicative ((<*>), (<$>))
+import Data.Text (Text)
+
+data Award
+  = Award Text Int (Maybe Text)
+  deriving (Show, Eq)
+
+instance FromJSON Award where
+  parseJSON (Object response) = do
+    Award <$> (response .: "name")
+          <*> (response .: "date_awarded")
+          <*> (response .:? "url")
+  parseJSON _ = mzero
diff --git a/lib/Web/XING/Types/BirthDate.hs b/lib/Web/XING/Types/BirthDate.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/BirthDate.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Web.XING.Types.BirthDate
+    (
+      BirthDate(..)
+    ) where
+
+import Data.Aeson (Value(..), FromJSON(..), (.:), (.:?))
+import Control.Monad (mzero)
+
+data BirthDate
+  = FullDate Integer Int Int
+  | DayOnly Int Int
+  deriving (Eq, Show)
+
+instance FromJSON BirthDate where
+  parseJSON (Object response) = do
+    maybeYear <- response .:? "year"
+    month     <- response .:  "month"
+    day       <- response .:  "day"
+    case maybeYear of
+      Just year -> return $ FullDate year month day
+      _         -> return $ DayOnly day month
+  parseJSON _ = mzero
diff --git a/lib/Web/XING/Types/ProfessionalExperience.hs b/lib/Web/XING/Types/ProfessionalExperience.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/ProfessionalExperience.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Web.XING.Types.ProfessionalExperience
+    (
+      ProfessionalExperience(..)
+    ) where
+
+import Data.Aeson (Value(..), FromJSON(..), (.:))
+import Control.Monad (mzero)
+import Control.Applicative ((<*>), (<$>))
+import Data.Text (Text)
+
+data ProfessionalExperience
+  = ProfessionalExperience {
+      pe_title       :: Maybe Text
+    , pe_beginDate   :: Maybe Text
+    , pe_endDate     :: Maybe Text
+    , pe_careerLevel :: Maybe Text
+    , pe_description :: Maybe Text
+    , pe_name        :: Maybe Text
+    , pe_tag         :: Maybe Text
+    , pe_companySize :: Maybe Text
+    , pe_url         :: Maybe Text
+    , pe_industry    :: Text
+  }
+  deriving (Show, Eq)
+
+instance FromJSON ProfessionalExperience where
+  parseJSON (Object response) = do
+    ProfessionalExperience <$> (response .: "title")
+                           <*> (response .: "begin_date")
+                           <*> (response .: "end_date")
+                           <*> (response .: "career_level")
+                           <*> (response .: "description")
+                           <*> (response .: "name")
+                           <*> (response .: "tag")
+                           <*> (response .: "company_size")
+                           <*> (response .: "url")
+                           <*> (response .: "industry")
+  parseJSON _ = mzero
diff --git a/lib/Web/XING/Types/User.hs b/lib/Web/XING/Types/User.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/User.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Web.XING.Types.User
+    (
+      User(..)
+    , UserId
+    , PhotoUrls
+    ) where
+
+import Data.Text (Text)
+import Data.Map (Map)
+
+type UserId    = Text
+type PhotoUrls = Map Text Text
+
+class User a where
+  userId      :: a -> UserId
+  displayName :: a -> Text
+  permalink   :: a -> Text
+  photoUrls   :: a -> PhotoUrls
diff --git a/lib/Web/XING/Types/User/FullUser.hs b/lib/Web/XING/Types/User/FullUser.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/User/FullUser.hs
@@ -0,0 +1,161 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module Web.XING.Types.User.FullUser
+    (
+      FullUser
+    , UserList(..)
+    , Gender(..)
+    , Language, Skill
+    --
+    , birthDate, gender, firstName, lastName
+    , activeEmail, premiumServices, badges, languages
+    , wants, haves, interests, organisations, pageName
+    , privateAddress, businessAddress
+    ) where
+
+import Web.XING.Types.User
+import Web.XING.Types.BirthDate
+import Web.XING.Types.Address
+
+import Data.Aeson (Value(..), FromJSON(..), (.:), (.:?))
+import Data.Aeson.Types (parseMaybe)
+import Control.Monad (mzero)
+import Data.Text (Text)
+import Data.Time.LocalTime (TimeZone(..))
+import Data.Map (Map)
+import Control.Applicative ((<$>), (<*>))
+
+type Language = Text
+type Skill = Text
+
+data Gender
+  = Male
+  | Female
+  deriving (Eq, Show)
+
+instance FromJSON Gender where
+  parseJSON (String "m") = return Male
+  parseJSON (String "f") = return Female
+  parseJSON _            = mzero
+
+newtype UserList = UserList { unUserList :: [FullUser] }
+  deriving (Show)
+
+-- TODO: it would be nice, if instead of using the UserList hack, we could use:
+--   instance FromJSON [FullUser] where
+instance FromJSON UserList where
+  parseJSON (Object response) = do
+    users <- parseJSON =<< (response .: "users")
+    return $ UserList users
+  parseJSON _ = mzero
+
+data FullUser
+  = FullUser {
+      _userId          :: UserId
+    , _displayName     :: Text
+    , _permalink       :: Text
+    , _firstName       :: Text
+    , _lastName        :: Text
+    , _pageName        :: Text
+    , _gender          :: Gender
+    , _activeEmail     :: Maybe Text
+    , _timeZone        :: TimeZone
+    , _premiumServices :: [Text]
+    , _badges          :: [Text]
+    , _languages       :: Map Language (Maybe Skill)
+    , _wants           :: Maybe Text
+    , _haves           :: Maybe Text
+    , _interests       :: Maybe Text
+    , _organisations   :: Maybe Text
+    , _privateAddress  :: Address
+    , _businessAddress :: Address
+    , _photoUrls       :: PhotoUrls
+    , _birthDate       :: Maybe BirthDate
+  }
+  deriving (Show, Eq)
+
+instance User FullUser where
+  userId      = _userId
+  displayName = _displayName
+  permalink   = _permalink
+  photoUrls   = _photoUrls
+
+instance FromJSON FullUser where
+  parseJSON (Object response) = do
+    FullUser <$> (response .: "id")
+             <*> (response .: "display_name")
+             <*> (response .: "permalink")
+             <*> (response .: "first_name")
+             <*> (response .: "last_name")
+             <*> (response .: "page_name")
+             <*> (parseJSON =<< response .: "gender")
+             <*> (response .:? "active_email")
+             <*> (response .: "time_zone" >>= \zone -> do
+                    TimeZone <$> (return . (60 *) =<< zone .: "utc_offset")
+                             <*> return False
+                             <*> (zone .: "name"))
+             <*> (response .: "premium_services")
+             <*> (response .: "badges")
+             <*> (response .: "languages")
+             <*> (response .: "wants")
+             <*> (response .: "haves")
+             <*> (response .: "interests")
+             <*> (response .: "organisation_member")
+             <*> (response .: "private_address")
+             <*> (response .: "business_address")
+             <*> (response .: "photo_urls")
+             <*> (return . (parseMaybe parseJSON) =<< response .: "birth_date")
+  parseJSON _ = mzero
+
+birthDate
+  :: FullUser
+  -> Maybe BirthDate
+birthDate = _birthDate
+
+gender
+  :: FullUser
+  -> Gender
+gender = _gender
+
+firstName, lastName
+  :: FullUser
+  -> Text
+firstName = _firstName
+lastName  = _lastName
+
+activeEmail
+  :: FullUser
+  -> Maybe Text
+activeEmail = _activeEmail
+
+premiumServices, badges
+  :: FullUser
+  -> [Text]
+premiumServices = _premiumServices
+badges          = _badges
+
+languages
+  :: FullUser
+  -> Map Language (Maybe Skill)
+languages = _languages
+
+wants, haves, interests, organisations
+  :: FullUser
+  -> Maybe Text
+wants         = _wants
+haves         = _haves
+interests     = _interests
+organisations = _organisations
+
+pageName
+  :: FullUser
+  -> Text
+pageName = _pageName
+
+privateAddress, businessAddress
+  :: FullUser
+  -> Address
+privateAddress  = _privateAddress
+businessAddress = _businessAddress
diff --git a/lib/Web/XING/Types/User/MinimalUser.hs b/lib/Web/XING/Types/User/MinimalUser.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/XING/Types/User/MinimalUser.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Web.XING.Types.User.MinimalUser
+    (
+      MinimalUser(..)
+    ) where
+
+import Web.XING.Types.User
+import Data.Aeson (Value(..), FromJSON(..), (.:))
+import Control.Applicative ((<$>), (<*>))
+import Data.Text (Text)
+
+data MinimalUser
+  = MinimalUser
+      UserId
+      Text
+      Text
+      PhotoUrls
+  deriving (Show, Eq)
+
+instance User MinimalUser where
+  userId (MinimalUser uid _ _ _ )      = uid
+  displayName (MinimalUser _ name _ _) = name
+  permalink (MinimalUser _ _ link _)   = link
+  photoUrls (MinimalUser _ _ _ urls)   = urls
+
+instance FromJSON MinimalUser where
+  parseJSON (Object response) = do
+    container <- response .: "id_card"
+    MinimalUser <$> (container .: "id")
+           <*> (container .: "display_name")
+           <*> (container .: "permalink")
+           <*> (container .: "photo_urls")
+  parseJSON _ = fail "no parse"
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,14 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+module Main where
+
+import Test.Framework
+
+import {-@ HTF_TESTS @-} Types.AddressTest
+import {-@ HTF_TESTS @-} Types.AwardTest
+import {-@ HTF_TESTS @-} Types.BirthDateTest hiding (main)
+import {-@ HTF_TESTS @-} Types.ProfessionalExperienceTest hiding (main)
+import {-@ HTF_TESTS @-} Types.User.FullUserTest hiding (main)
+import {-@ HTF_TESTS @-} Types.User.MinimalUserTest hiding (main)
+
+main :: IO ()
+main = htfMain htf_importedTests
diff --git a/tests/Types/AddressTest.hs b/tests/Types/AddressTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/AddressTest.hs
@@ -0,0 +1,88 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.AddressTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+import Data.Text.Lazy.Encoding (encodeUtf8)
+
+-- valid fields
+
+test_noContent :: IO ()
+test_noContent
+  = assertValidAddress
+      "  {                           \
+      \     \"street\":       \"\",  \
+      \     \"zip_code\":     \"\",  \
+      \     \"city\":         \"\",  \
+      \     \"province\":     \"\",  \
+      \     \"country\":      \"\",  \
+      \     \"email\":        \"\",  \
+      \     \"phone\":        \"\",  \
+      \     \"fax\":          \"\",  \
+      \     \"mobile_phone\": \"\"   \
+      \  }                           "
+
+test_nullValuesAreValid :: IO ()
+test_nullValuesAreValid
+  = assertValidAddress
+      "  {                           \
+      \     \"street\":       null,  \
+      \     \"zip_code\":     null,  \
+      \     \"city\":         null,  \
+      \     \"province\":     null,  \
+      \     \"country\":      null,  \
+      \     \"email\":        null,  \
+      \     \"phone\":        null,  \
+      \     \"fax\":          null,  \
+      \     \"mobile_phone\": null   \
+      \  }                           "
+
+test_fullAddress :: IO ()
+test_fullAddress
+  = assertValidAddress . encodeUtf8 $
+      "  {                                           \
+      \     \"street\":       \"Privatstraße 1\",    \
+      \     \"zip_code\":     \"20357\",             \
+      \     \"city\":         \"Hamburg\",           \
+      \     \"province\":     \"Hamburg\",           \
+      \     \"country\":      \"DE\",                \
+      \     \"email\":        \"max@mustermann.de\", \
+      \     \"phone\":        \"49|40|1234560\",     \
+      \     \"fax\":          \"||\",                \
+      \     \"mobile_phone\": \"49|0155|1234567\"    \
+      \  }                                           "
+
+-- invalid fields
+
+test_emptyHashNotValid :: IO ()
+test_emptyHashNotValid
+  = assertInvalidAddress
+      "{}"
+
+test_someFieldsMissing :: IO ()
+test_someFieldsMissing
+  = assertInvalidAddress
+      "  {                           \
+      \     \"street\":       \"\",  \
+      \     \"zip_code\":     \"\",  \
+      \     \"city\":         \"\",  \
+      \     \"province\":     \"\",  \
+      \     \"country\":      \"\",  \
+      \     \"email\":        \"\"   \
+      \  }                           "
+
+parseAddress
+  :: BSL.ByteString
+  -> Maybe Address
+parseAddress = decode
+
+assertValidAddress, assertInvalidAddress
+  :: BSL.ByteString
+  -> IO ()
+assertValidAddress   = assertBool . isJust    . parseAddress
+assertInvalidAddress = assertBool . isNothing . parseAddress
diff --git a/tests/Types/AwardTest.hs b/tests/Types/AwardTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/AwardTest.hs
@@ -0,0 +1,47 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.AwardTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+
+-- valid fields
+
+noContentAward :: BSL.ByteString
+noContentAward
+  = "  {                          \
+    \     \"name\":         \"\", \
+    \     \"date_awarded\": 2007, \
+    \     \"url\":          null  \
+    \  }                          "
+
+test_noContent :: IO ()
+test_noContent
+  = assertValidAward noContentAward
+
+test_noContentMappedCorrect :: IO ()
+test_noContentMappedCorrect
+  = assertEqual (Award "" 2007 Nothing) (fromJust.parseAward $ noContentAward)
+
+test_fullAwardMappedCorrect :: IO ()
+test_fullAwardMappedCorrect
+  = assertEqual (Award "Spacewalker" 1998 (Just "http://nasa.gov")) ((fromJust.parseAward)
+      "  {                                         \
+      \     \"name\":         \"Spacewalker\",     \
+      \     \"url\":          \"http://nasa.gov\", \
+      \     \"date_awarded\": 1998                 \
+      \  }                                         ")
+
+parseAward
+  :: BSL.ByteString
+  -> Maybe Award
+parseAward = decode
+
+assertValidAward
+  :: BSL.ByteString
+  -> IO ()
+assertValidAward = assertBool . isJust . parseAward
diff --git a/tests/Types/BirthDateTest.hs b/tests/Types/BirthDateTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/BirthDateTest.hs
@@ -0,0 +1,70 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.BirthDateTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+
+-- Valid BirthDate
+
+test_fullBirthDateCorrectMapped :: IO ()
+test_fullBirthDateCorrectMapped
+  = assertEqual (FullDate 1963 8 12) (fromJust $ parseBirthDate fullBirthDate)
+  where
+    fullBirthDate =
+      "  {                  \
+      \    \"day\":     12, \
+      \    \"month\":    8, \
+      \    \"year\":  1963  \
+      \  }                  "
+
+test_dayOnlyCorrectMapped :: IO ()
+test_dayOnlyCorrectMapped
+  = assertEqual (DayOnly 12 8) (fromJust $ parseBirthDate dayOnly)
+  where
+    dayOnly =
+      "  {                  \
+      \    \"day\":     12, \
+      \    \"month\":    8, \
+      \    \"year\":  null  \
+      \  }                  "
+
+-- Invalid BirthDate
+
+test_emptyHash :: IO ()
+test_emptyHash
+  = assertInvalidBirthDate "{}"
+
+test_nullValuesAreNotAValidBirthDate :: IO ()
+test_nullValuesAreNotAValidBirthDate
+  = assertInvalidBirthDate
+      "  {                  \
+      \    \"day\":   null, \
+      \    \"month\": null, \
+      \    \"year\":  null  \
+      \  }                  "
+
+test_incompleteHash :: IO ()
+test_incompleteHash
+  = assertInvalidBirthDate
+      "  {                  \
+      \    \"day\":     12, \
+      \    \"year\":  1963  \
+      \  }                  "
+
+parseBirthDate
+  :: BSL.ByteString
+  -> Maybe BirthDate
+parseBirthDate = decode
+
+assertInvalidBirthDate
+  :: BSL.ByteString
+  -> IO ()
+assertInvalidBirthDate = assertBool . isNothing . parseBirthDate
+
+main :: IO ()
+main = htfMain htf_thisModulesTests
diff --git a/tests/Types/ProfessionalExperienceTest.hs b/tests/Types/ProfessionalExperienceTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/ProfessionalExperienceTest.hs
@@ -0,0 +1,93 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.ProfessionalExperienceTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+
+-- valid fields
+
+test_noContentValid :: IO ()
+test_noContentValid
+  = assertValidProfessionalExperience
+      "  {                              \
+      \    \"title\":        null,      \
+      \    \"begin_date\":   null,      \
+      \    \"end_date\":     null,      \
+      \    \"career_level\": null,      \
+      \    \"description\":  null,      \
+      \    \"name\":         null,      \
+      \    \"tag\":          null,      \
+      \    \"company_size\": null,      \
+      \    \"url\":          null,      \
+      \    \"industry\":     \"OTHERS\" \
+      \  }                              "
+
+fullProfessionalExperience :: BSL.ByteString
+fullProfessionalExperience =
+      "  {                                                 \
+      \    \"title\":        \"Softwareentwickler\",       \
+      \    \"begin_date\":   \"2010-01\",                  \
+      \    \"end_date\":     \"2012-12\",                  \
+      \    \"career_level\": \"PROFESSIONAL_EXPERIENCED\", \
+      \    \"description\":  \"awesome description\",      \
+      \    \"name\":         \"XING AG\",                  \
+      \    \"tag\":          \"XINGAG\",                   \
+      \    \"company_size\": \"201-500\",                  \
+      \    \"url\":          \"https://www.xing.com\",     \
+      \    \"industry\":     \"OTHERS\"                    \
+      \  }                                                 "
+
+test_fullProfessionalExperienceValid :: IO ()
+test_fullProfessionalExperienceValid
+  = assertValidProfessionalExperience fullProfessionalExperience
+
+test_contentMappedCorrect :: IO ()
+test_contentMappedCorrect
+  = assertEqual (ProfessionalExperience
+      (Just "Softwareentwickler")
+      (Just "2010-01")
+      (Just "2012-12")
+      (Just "PROFESSIONAL_EXPERIENCED")
+      (Just "awesome description")
+      (Just "XING AG")
+      (Just "XINGAG")
+      (Just "201-500")
+      (Just "https://www.xing.com")
+      "OTHERS") (fromJust $ parseProfessionalExperience fullProfessionalExperience)
+
+-- invalid fields
+
+test_emptyHashNotValid :: IO ()
+test_emptyHashNotValid
+  = assertInvalidProfessionalExperience
+      "{}"
+
+test_someFieldsMissing :: IO ()
+test_someFieldsMissing
+  = assertInvalidProfessionalExperience
+      "  {                              \
+      \    \"name\":         null,      \
+      \    \"tag\":          null,      \
+      \    \"company_size\": null,      \
+      \    \"url\":          null,      \
+      \    \"industry\":     \"OTHERS\" \
+      \  }                              "
+
+parseProfessionalExperience
+  :: BSL.ByteString
+  -> Maybe ProfessionalExperience
+parseProfessionalExperience = decode
+
+assertValidProfessionalExperience, assertInvalidProfessionalExperience
+  :: BSL.ByteString
+  -> IO ()
+assertValidProfessionalExperience   = assertBool . isJust    . parseProfessionalExperience
+assertInvalidProfessionalExperience = assertBool . isNothing . parseProfessionalExperience
+
+main :: IO ()
+main = htfMain htf_thisModulesTests
diff --git a/tests/Types/User/FullUserTest.hs b/tests/Types/User/FullUserTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/User/FullUserTest.hs
@@ -0,0 +1,88 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.User.FullUserTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+
+test_noContentValid :: IO ()
+test_noContentValid
+  = (assertBool . isJust . parseFullUser) fullUserWithoutContent
+
+-- https://dev.xing.com/docs/user_profile
+fullUserWithoutContent :: BSL.ByteString
+fullUserWithoutContent =
+  "  {                                \
+  \    \"id\":           \"\",        \
+  \    \"display_name\": \"\",        \
+  \    \"permalink\":    \"\",        \
+  \    \"first_name\":   \"\",        \
+  \    \"last_name\":    \"\",        \
+  \    \"page_name\":    \"\",        \
+  \    \"gender\":       \"m\",       \
+  \    \"active_email\": null,        \
+  \    \"time_zone\": {               \
+  \      \"utc_offset\": 2.0,         \
+  \      \"name\": \"Europe/Berlin\"  \
+  \    },                             \
+  \    \"premium_services\": [],      \
+  \    \"badges\":           [],      \
+  \    \"languages\": {},             \
+  \    \"wants\": \"\",               \
+  \    \"haves\": null,               \
+  \    \"interests\": null,           \
+  \    \"organisation_member\": null, \
+  \    \"private_address\": {         \
+  \      \"street\":       \"\",      \
+  \      \"zip_code\":     \"\",      \
+  \      \"city\":         \"\",      \
+  \      \"province\":     \"\",      \
+  \      \"country\":      \"\",      \
+  \      \"email\":        \"\",      \
+  \      \"phone\":        \"\",      \
+  \      \"fax\":          \"\",      \
+  \      \"mobile_phone\": \"\"       \
+  \    },                             \
+  \    \"business_address\": {        \
+  \      \"street\":       \"\",      \
+  \      \"zip_code\":     \"\",      \
+  \      \"city\":         \"\",      \
+  \      \"province\":     \"\",      \
+  \      \"country\":      \"\",      \
+  \      \"email\":        \"\",      \
+  \      \"phone\":        \"\",      \
+  \      \"fax\":          \"\",      \
+  \      \"mobile_phone\": \"\"       \
+  \    },                             \
+  \    \"professional_experience\": {     \
+  \      \"primary_company\": {           \
+  \        \"title\":        null,        \
+  \        \"begin_date\":   null,        \
+  \        \"end_date\":     null,        \
+  \        \"career_level\": null,        \
+  \        \"description\":  null,        \
+  \                                       \
+  \        \"name\":         null,        \
+  \        \"tag\":          null,        \
+  \        \"company_size\": null,        \
+  \        \"url\":          null,        \
+  \        \"industry\":     \"OTHERS\"   \
+  \      },                               \
+  \      \"non_primary_companies\": [],   \
+  \      \"awards\": []                   \
+  \    },                                 \
+  \    \"photo_urls\": {},            \
+  \    \"birth_date\": {}             \
+  \  }                                "
+
+parseFullUser
+  :: BSL.ByteString
+  -> Maybe FullUser
+parseFullUser = decode
+
+main :: IO ()
+main = htfMain htf_thisModulesTests
diff --git a/tests/Types/User/MinimalUserTest.hs b/tests/Types/User/MinimalUserTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/Types/User/MinimalUserTest.hs
@@ -0,0 +1,38 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types.User.MinimalUserTest where
+
+import Test.Framework
+import Web.XING
+import Data.Maybe
+import Data.Map
+import Data.Aeson
+import qualified Data.ByteString.Lazy as BSL
+
+test_noContentValid :: IO ()
+test_noContentValid
+  = (assertBool . isJust . parseMinimalUser) minimalUserWithoutContent
+
+test_minimalUserCorrectMapped :: IO ()
+test_minimalUserCorrectMapped
+  = assertEqual (MinimalUser "" "" "" empty) (fromJust $ parseMinimalUser minimalUserWithoutContent)
+
+minimalUserWithoutContent :: BSL.ByteString
+minimalUserWithoutContent =
+  "  {                           \
+  \    \"id_card\": {            \
+  \      \"id\":           \"\", \
+  \      \"display_name\": \"\", \
+  \      \"permalink\":    \"\", \
+  \      \"photo_urls\":   {}    \
+  \    }                         \
+  \  }                           "
+
+parseMinimalUser
+  :: BSL.ByteString
+  -> Maybe MinimalUser
+parseMinimalUser = decode
+
+main :: IO ()
+main = htfMain htf_thisModulesTests
diff --git a/xing-api.cabal b/xing-api.cabal
new file mode 100644
--- /dev/null
+++ b/xing-api.cabal
@@ -0,0 +1,143 @@
+name:                xing-api
+version:             0.1.1
+synopsis:            Wrapper for the XING API, v1.
+description:         This package is currently under development and not considered stable.
+                     The versioning follows <http://semver.org> and the first stable version will be release as 1.0.0.
+                     .
+                     This package includes a couple of demo programs.
+                     By default these demos won't be built and you'll only install the library.
+                     You have to set the /demos/ flag if you want to install them.
+                     To use these demos, you also have to obtain an API consumer key from
+                     <https://dev.xing.com/applications> (a /test key/ will suffice).
+                     .
+                     >cabal install -f demos xing-api
+license:             BSD3
+license-file:        LICENSE
+category:            Web
+copyright:           Copyright 2013 Jan Ahrens
+author:              Jan Ahrens
+maintainer:          Jan Ahrens
+cabal-version:       >=1.10
+homepage:            http://github.com/JanAhrens/xing-api-haskell
+bug-reports:         http://github.com/JanAhrens/xing-api-haskell/issues
+build-type:          Simple
+
+flag demos
+  default:           False
+  description:       Build demo programs
+
+flag minimal-demo
+  default:           False
+  description:       Build the minimal demo from the README. It's not runnable unless you modify the source.
+
+source-repository head
+  type:              git
+  location:          http://github.com/JanAhrens/xing-api-haskell.git
+
+library
+  exposed-modules:     Web.XING
+
+  build-depends:       base                    == 4.5.*
+                     , containers              == 0.4.*
+                     , time                    == 1.4.*
+                     , text                    == 0.11.*
+                     , authenticate-oauth      == 1.4.*
+                     , http-types              == 0.8.*
+                     , http-conduit            == 1.8.*
+                     , resourcet               == 0.4.*
+                     , transformers            == 0.2.*
+                     , text                    == 0.11.*
+                     , bytestring              == 0.9.*
+                     , aeson                   == 0.6.*
+                     , lifted-base             == 0.2.*
+                     , monad-control           == 0.3.*
+
+  other-modules:       Web.XING.API
+                     , Web.XING.API.Error
+                     , Web.XING.Calls.IdCard
+                     , Web.XING.Calls.User
+                     , Web.XING.Internal.AuthenticateOAuthPatch
+                     , Web.XING.Types
+                     , Web.XING.Types.User
+                     , Web.XING.Types.User.MinimalUser
+                     , Web.XING.Types.User.FullUser
+                     , Web.XING.Types.Address
+                     , Web.XING.Types.Award
+                     , Web.XING.Types.BirthDate
+                     , Web.XING.Types.ProfessionalExperience
+                     , Web.XING.Auth
+
+  hs-source-dirs:    lib
+  ghc-options:       -Wall
+  default-language:  Haskell2010
+
+Test-Suite TestMain
+  type:              exitcode-stdio-1.0
+  build-depends:       base                    == 4.5.*
+                     , HTF                     == 0.10.*
+                     , text                    == 0.11.*
+                     , bytestring              == 0.9.*
+                     , aeson                   == 0.6.*
+                     , containers              == 0.4.*
+                     , time                    == 1.4.*
+                     , xing-api
+  other-modules:       Types.AddressTest
+                     , Types.AwardTest
+                     , Types.BirthDateTest
+                     , Types.ProfessionalExperienceTest
+                     , Types.User.FullUserTest
+                     , Types.User.MinimalUserTest
+  hs-source-dirs:    tests
+  main-is:           Main.hs
+  ghc-options:       -Wall
+  default-language:  Haskell2010
+
+executable xing-api-cli-demo
+  if flag(demos)
+    buildable:       True
+    build-depends:     base                    == 4.*
+                     , bytestring              == 0.9.*
+                     , monad-control           == 0.3.*
+                     , resourcet               == 0.4.*
+                     , text                    == 0.11.*
+                     , xing-api
+  else
+    buildable:       False
+  hs-source-dirs:    demos
+  main-is:           cli-demo.hs
+  GHC-options:       -Wall
+  Default-language:  Haskell2010
+
+executable xing-api-yesod-demo
+  hs-source-dirs:    demos
+  if flag(demos)
+    buildable:       True
+    build-depends:     base                    == 4.*
+                     , bytestring              == 0.9.*
+                     , containers              == 0.4.*
+                     , hamlet                  == 1.1.*
+                     , http-conduit            == 1.8.*
+                     , shakespeare-i18n        == 1.0.*
+                     , text                    == 0.11.*
+                     , time                    == 1.4.*
+                     , warp                    == 1.3.*
+                     , xing-api
+                     , yesod-core              == 1.1.*
+  else
+    buildable:       False
+  main-is:           yesod-demo.hs
+  GHC-options:       -Wall
+  Default-language:  Haskell2010
+
+executable xing-api-minimal-demo
+  hs-source-dirs:    demos
+  if flag(minimal-demo)
+    buildable:       True
+    build-depends:     base                    == 4.*
+                     , bytestring              == 0.9.*
+                     , xing-api
+  else
+    buildable:       False
+  main-is:           minimal-demo.hs
+  GHC-options:       -Wall
+  Default-language:  Haskell2010
