diff --git a/VKHS.cabal b/VKHS.cabal
--- a/VKHS.cabal
+++ b/VKHS.cabal
@@ -1,6 +1,6 @@
 
 name:                VKHS
-version:             1.6.0
+version:             1.6.1
 synopsis:            Provides access to Vkontakte social network via public API
 description:
     Provides access to Vkontakte API methods. Library requires no interaction
@@ -9,7 +9,7 @@
 license:             BSD3
 license-file:        LICENSE
 author:              Sergey Mironov
-maintainer:          ierton@gmail.com
+maintainer:          grrwlf@gmail.com
 copyright:           Copyright (c) 2012, Sergey Mironov
 category:            Web
 build-type:          Simple
@@ -26,7 +26,9 @@
                      Web.VKHS.Monad
                      Web.VKHS.Client
                      Web.VKHS.Login
+                     Web.VKHS.Error
                      Web.VKHS.API
+                     Web.VKHS.API.Types
 
   build-depends:     base >=4.6 && <5,
                      containers,
diff --git a/src/Web/VKHS/API/Types.hs b/src/Web/VKHS/API/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/VKHS/API/Types.hs
@@ -0,0 +1,180 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module Web.VKHS.API.Types where
+
+import Data.Typeable
+import Data.Data
+import Data.Time.Clock
+import Data.Time.Clock.POSIX
+
+import Data.Aeson ((.=), (.:), (.:?), (.!=), FromJSON(..))
+import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Types as Aeson
+
+import Data.Vector as Vector (head, tail)
+import Data.Text
+
+import Text.Printf
+
+-- See http://vk.com/developers.php?oid=-1&p=Авторизация_клиентских_приложений
+-- (in Russian) for more details
+
+data Response a = Response {
+    resp_json :: Aeson.Value
+  , resp_data :: a
+  }
+  deriving (Show, Data, Typeable)
+
+parseJSON_obj_error :: String -> Aeson.Value -> Aeson.Parser a
+parseJSON_obj_error name o = fail $
+  printf "parseJSON: %s expects object, got %s" (show name) (show o)
+
+instance (FromJSON a) => FromJSON (Response a) where
+  parseJSON j = Aeson.withObject "Response" (\o ->
+    Response <$> pure j <*> o .: "response") j
+
+-- Deprecated
+data SizedList a = SizedList Int [a]
+  deriving(Show, Data, Typeable)
+
+instance (FromJSON a) => FromJSON (SizedList a) where
+  parseJSON = Aeson.withArray "SizedList" $ \v -> do
+    n <- Aeson.parseJSON (Vector.head v)
+    t <- Aeson.parseJSON (Aeson.Array (Vector.tail v))
+    return (SizedList n t)
+
+data MusicRecord = MusicRecord
+  { mr_id :: Int
+  , mr_owner_id :: Int
+  , mr_artist :: String
+  , mr_title :: String
+  , mr_duration :: Int
+  , mr_url_str :: String
+  } deriving (Show, Data, Typeable)
+
+instance FromJSON MusicRecord where
+  parseJSON = Aeson.withObject "MusicRecord" (\o ->
+    MusicRecord
+      <$> (o .: "aid")
+      <*> (o .: "owner_id")
+      <*> (o .: "artist")
+      <*> (o .: "title")
+      <*> (o .: "duration")
+      <*> (o .: "url"))
+
+
+data UserRecord = UserRecord
+  { ur_id :: Int
+  , ur_first_name :: String
+  , ur_last_name :: String
+  , ur_photo :: String
+  , ur_university :: Maybe Int
+  , ur_university_name :: Maybe String
+  , ur_faculty :: Maybe Int
+  , ur_faculty_name :: Maybe String
+  , ur_graduation :: Maybe Int
+  } deriving (Show, Data, Typeable)
+
+
+data WallRecord = WallRecord
+  { wr_id :: Int
+  , wr_to_id :: Int
+  , wr_from_id :: Int
+  , wr_wtext :: String
+  , wr_wdate :: Int
+  } deriving (Show)
+
+publishedAt :: WallRecord -> UTCTime
+publishedAt wr = posixSecondsToUTCTime $ fromIntegral $ wr_wdate wr
+
+{-
+ - API version 5.44
+ -}
+
+data Many a = Many {
+    m_count :: Int
+  , m_items :: a
+  } deriving (Show)
+
+instance FromJSON a => FromJSON (Many a) where
+  parseJSON = Aeson.withObject "Result" (\o ->
+    Many <$> o .: "count" <*> o .: "items")
+
+
+data Deact = Banned | Deleted | OtherDeact Text
+  deriving(Show,Eq,Ord)
+
+instance FromJSON Deact where
+  parseJSON = Aeson.withText "Deact" $ \x ->
+    return $ case x of
+              "deleted" -> Deleted
+              "banned" -> Banned
+              x -> OtherDeact x
+
+data GroupType = Group | Event | Public
+  deriving(Show,Eq,Ord)
+
+instance FromJSON GroupType where
+  parseJSON = Aeson.withText "GroupType" $ \x ->
+    return $ case x of
+              "group" -> Group
+              "page" -> Public
+              "event" -> Event
+
+
+data GroupIsClosed = GroupOpen | GroupClosed | GroupPrivate
+  deriving(Show,Eq,Ord,Enum)
+
+data GroupRecord = GroupRecord {
+    gr_id :: Int
+  , gr_name :: Text
+  , gr_screen_name :: Text
+  , gr_is_closed :: GroupIsClosed
+  , gr_deact :: Maybe Deact
+  , gr_is_admin :: Int
+  , gr_admin_level :: Maybe Int
+  , gr_is_member :: Bool
+  , gr_member_status :: Maybe Int
+  , gr_invited_by :: Maybe Int
+  , gr_type :: GroupType
+  , gr_has_photo :: Bool
+  , gr_photo_50 :: String
+  , gr_photo_100 :: String
+  , gr_photo_200 :: String
+  -- arbitrary fields
+  , gr_can_post :: Maybe Bool
+  , gr_members_count :: Maybe Int
+  } deriving (Show)
+
+instance FromJSON GroupRecord where
+  parseJSON = Aeson.withObject "GroupRecord" $ \o ->
+    GroupRecord
+      <$> (o .: "id")
+      <*> (o .: "name")
+      <*> (o .: "screen_name")
+      <*> fmap toEnum (o .: "is_closed")
+      <*> (o .:? "deactivated")
+      <*> (o .:? "is_admin" .!= 0)
+      <*> (o .:? "admin_level")
+      <*> fmap (==1) (o .:? "is_member" .!= (0::Int))
+      <*> (o .:? "member_status")
+      <*> (o .:? "invited_by")
+      <*> (o .: "type")
+      <*> (o .:? "has_photo" .!= False)
+      <*> (o .: "photo_50")
+      <*> (o .: "photo_100")
+      <*> (o .: "photo_200")
+      <*> (fmap (==(1::Int)) <$> (o .:? "can_post"))
+      <*> (o .:? "members_count")
+
+
+groupURL :: GroupRecord -> String
+groupURL GroupRecord{..} = "https://vk.com/" ++ urlify gr_type ++ (show gr_id) where
+  urlify Group = "club"
+  urlify Event = "event"
+  urlify Public = "page"
+
+
diff --git a/src/Web/VKHS/Error.hs b/src/Web/VKHS/Error.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/VKHS/Error.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Web.VKHS.Error where
+
+import Web.VKHS.Types
+import Web.VKHS.Client (Response, Request, URL)
+import qualified Web.VKHS.Client as Client
+import Data.ByteString.Char8 (ByteString, unpack)
+
+data Error = ETimeout | EClient Client.Error
+  deriving(Show, Eq)
+
+type R t a = Result t a
+
+data Result t a =
+    Fine a
+  | UnexpectedInt Error (Int -> t (R t a) (R t a))
+  | UnexpectedBool Error (Bool -> t (R t a) (R t a))
+  | UnexpectedURL Client.Error (URL -> t (R t a) (R t a))
+  | UnexpectedRequest Client.Error (Request -> t (R t a) (R t a))
+  | UnexpectedResponse Client.Error (Response -> t (R t a) (R t a))
+  | UnexpectedFormField Form String (String -> t (R t a) (R t a))
+  | LoginActionsExhausted
+  | RepeatedForm Form (() -> t (R t a) (R t a))
+  | JSONParseFailure ByteString (JSON -> t (R t a) (R t a))
+  | JSONParseFailure' JSON String
+
+data ResultDescription a =
+    DescFine a
+  | DescError String
+  deriving(Show)
+
+describeResult :: (Show a) => Result t a -> String
+describeResult (Fine a) = "Fine " ++ show a
+describeResult (UnexpectedInt e k) = "UnexpectedInt " ++ (show e)
+describeResult (UnexpectedBool e k) = "UnexpectedBool " ++  (show e)
+describeResult (UnexpectedURL e k) = "UnexpectedURL " ++ (show e)
+describeResult (UnexpectedRequest e k) = "UnexpectedRequest " ++ (show e)
+describeResult LoginActionsExhausted = "LoginActionsExhausted"
+describeResult (RepeatedForm f k) = "RepeatedForm"
+describeResult (JSONParseFailure bs _) = "JSONParseFailure " ++ (show bs)
+describeResult (JSONParseFailure' JSON{..} s) = "JSONParseFailure' " ++ (show s) ++ " JSON: " ++ (take 1000 $ show js_aeson)
+
