diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) <year> <copyright holders>
+
+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/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/src/Json.hs b/src/Json.hs
new file mode 100644
--- /dev/null
+++ b/src/Json.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module Json
+  ( jsonOptionsForPrefix
+  , deriveJSON
+  , deriveJSON_
+  , deriveJSONOptions
+  , deriveEnumJSON
+  ) where
+
+import Data.Aeson (FromJSON(..), ToJSON(..), Value(..))
+import qualified Data.Aeson.TH as J (Options (..), defaultOptions, deriveJSON)
+import Data.Char (toLower, isUpper)
+import Data.Text as T (unpack)
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax (showName)
+
+jsonOptionsForPrefix :: String -> J.Options
+jsonOptionsForPrefix prefix =
+  J.defaultOptions
+    { J.fieldLabelModifier = underscoreIt . (drop $ length prefix) }
+
+underscoreIt :: String -> String
+underscoreIt s = go [] s
+  where
+    go :: String -> String -> String
+    go [] (c:cs) = go [toLower c] cs
+    go r (c:cs) | isUpper c = go (r ++ "_" ++ [(toLower c)]) cs
+    go r (c:cs) = go (r ++ [c]) cs
+    go r [] = r
+
+deriveJSONOptions :: J.Options -> Name -> Q [Dec]
+deriveJSONOptions = J.deriveJSON
+
+deriveJSON :: Name -> Q [Dec]
+deriveJSON name = deriveJSONOptions (jsonOptionsForPrefix (nameBase name)) name
+
+deriveJSON_ :: Name -> Q [Dec]
+deriveJSON_ name = deriveJSONOptions (jsonOptionsForPrefix ('_' : nameBase name)) name
+
+deriveEnumJSON :: Name -> Q [Dec]
+deriveEnumJSON name = [d|
+  instance FromJSON $(conT name) where
+    parseJSON (String s) = return $ read . T.unpack $ s
+    parseJSON _ = fail $ "Invalid " ++ $(stringE (showName name))
+
+  instance ToJSON $(conT name) where
+    toJSON = toJSON . show
+  |]
diff --git a/src/Zendesk.hs b/src/Zendesk.hs
new file mode 100644
--- /dev/null
+++ b/src/Zendesk.hs
@@ -0,0 +1,161 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverlappingInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE IncoherentInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Zendesk
+( runZendesk
+, createUser
+, getUsers
+, getTickets
+, getTicketFields
+, ZendeskConfig(..)
+, runZendeskT
+) where
+
+import Control.Applicative ((<$>), (<*>))
+import Control.Exception (catches, SomeException(..), Handler(..))
+import Control.Failure (Failure(..))
+import Control.Monad (liftM, forM)
+import Control.Monad.Reader (ReaderT (..), asks)
+import Control.Monad.Error (ErrorT (..), MonadError(..), Error(..))
+import Control.Monad.IO.Class (MonadIO(..))
+import Control.Monad.Logger (MonadLogger(..), logDebug)
+import Control.Monad.Trans (lift)
+import Data.Aeson as J (eitherDecode, encode, FromJSON(..), withObject, withText, (.:), object, (.=), Value(..), Object)
+import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as LBS (toStrict)
+import Data.ByteString.Char8 as BS8 (pack, unpack)
+import Data.CaseInsensitive (mk)
+import Data.Default (Default(..))
+import qualified Data.HashMap.Strict as M
+import Data.Int (Int64)
+import Data.Maybe (fromJust)
+import Data.PEM (PEM(..), pemParseBS)
+import Data.Text as T (pack, Text, unpack)
+import Data.Text.Encoding as T (encodeUtf8)
+import Data.Time.Clock (UTCTime(..))
+import Data.Traversable (Traversable(..))
+import Data.X509 (HashALG(..), decodeSignedCertificate)
+import Data.X509.CertificateStore (CertificateStore, makeCertificateStore)
+import Data.X509.Validation (ValidationChecks(..), validate, defaultHooks, defaultChecks)
+import Network.Connection (TLSSettings(..))
+import Network.HTTP.Client (getUri, applyBasicAuth)
+import Network.HTTP.Client.TLS (mkManagerSettings)
+import Network.HTTP.Conduit as HTTP ( httpLbs, parseUrl, withManagerSettings
+                                    , HttpException(..)
+                                    , Request(..), Response(..)
+                                    , responseBody, RequestBody(..), requestBody
+                                    , method, requestHeaders, checkStatus)
+import Network.HTTP.Types.Status (Status(..))
+import Network.TLS (Credential, Credentials(..), ClientParams(..), Shared(..), ClientHooks(..), Supported(..), defaultParamsClient, credentialLoadX509FromMemory)
+import Network.TLS.Extra.Cipher (ciphersuite_strong)
+
+import Json (deriveJSON, deriveJSON_, deriveEnumJSON)
+
+import Data.Conduit (Source, yield)
+
+import Zendesk.Common
+import Zendesk.User
+
+getTicketsUrl :: Monad m => ZendeskT m String
+getTicketsUrl = do
+  baseUrl <- asks zendeskUrl
+  return $ baseUrl ++ "/api/v2/tickets.json"
+
+getTicketFieldsUrl :: Monad m => ZendeskT m String
+getTicketFieldsUrl = do
+  baseUrl <- asks zendeskUrl
+  return $ baseUrl ++ "/api/v2/ticket_fields.json"
+
+
+data TicketField = TicketField
+  { ticketFieldId                  :: Maybe Int
+  , ticketFieldUrl                 :: Maybe Text
+  , ticketFieldType                :: Text
+  , ticketFieldTitle               :: Text
+  , ticketFieldDescription         :: Maybe Text
+  , ticketFieldPosition            :: Maybe Int
+  , ticketFieldActive              :: Maybe Bool
+  , ticketFieldRequired            :: Maybe Bool
+  , ticketFieldCollapsedForAgents  :: Maybe Bool
+  , ticketFieldRegexpForValidation :: Maybe Text
+  , ticketFieldTitleInPortal       :: Maybe Text
+  , ticketFieldVisibleInPortal     :: Maybe Bool
+  , ticketFieldEditableInPortal    :: Maybe Bool
+  , ticketFieldRequiredInPortal    :: Maybe Bool
+  , ticketFieldTag                 :: Maybe Text
+  , ticketFieldCreatedAt           :: Maybe UTCTime
+  , ticketFieldUpdatedAt           :: Maybe UTCTime
+  --, ticketSystemFieldOptions       :: Maybe [Text] TODO Array of something
+  --, ticketCustomFieldOptions       :: Maybe [Text] TODO Array of something
+  , ticketFieldRemovable           :: Maybe Bool
+  } deriving (Show)
+
+data TicketFieldValue = TicketFieldValue
+  { ticketFieldValueId    :: Int
+  , ticketFieldValueValue :: Maybe Text
+  } deriving (Show)
+
+data Ticket = Ticket
+  { ticketId :: Maybe Int
+  , ticketUrl :: Maybe Text
+  , ticketExternalId :: Maybe Text
+  , ticketType :: Maybe Text
+  , ticketSubject :: Maybe Text
+  , ticketDescription :: Maybe Text
+  , ticketPriority :: Maybe Text
+  , ticketStatus :: Maybe Text
+  , ticketRecipient :: Maybe Text
+  , ticketRequesterId :: Int
+  , ticketSubmitterId :: Maybe Int
+  , ticketAssigneeId :: Maybe Int
+  , ticketOrganizationId :: Maybe Int
+  , ticketGroupId :: Maybe Int
+  , ticketCollaboratorIds :: Maybe [Int]
+  , ticketForumTopicId :: Maybe Int
+  , ticketProblemId :: Maybe Int
+  , ticketHasIncidents :: Maybe Bool
+  , ticketDueAt :: Maybe Text
+  , ticketTags :: Maybe [Text]
+  , ticketVia :: Maybe Via
+  , ticketCustomFields :: Maybe [TicketFieldValue]
+  , ticketSatisfactionRaiting :: Maybe Object
+  , ticketSharingAgreementIds :: Maybe [Int]
+  , ticketFollowupIds :: Maybe [Int]
+  , ticketTicketFormId :: Maybe Int
+  , ticketBrandId :: Maybe Int
+  , ticketCreatedAt :: Maybe UTCTime
+  , ticketUpdatedAt :: Maybe UTCTime
+  } deriving (Show)
+
+data Via = Via
+  { viaChannel :: Text
+  , viaSource  :: Maybe Object
+  } deriving (Show)
+
+instance CollectionKey Ticket where
+  collectionKey _ = "tickets"
+
+instance CollectionKey TicketField where
+  collectionKey _ = "ticket_fields"
+
+deriveJSON ''Ticket
+deriveJSON ''TicketField
+deriveJSON ''TicketFieldValue
+deriveJSON ''Via
+
+getTickets :: (MonadIO m, MonadLogger m) => Source (ZendeskT m) Ticket
+getTickets =
+  getCollection =<< (Just `liftM` (T.pack `liftM` (lift getTicketsUrl)))
+
+getTicketFields :: (MonadIO m, MonadLogger m) => Source (ZendeskT m) TicketField
+getTicketFields =
+  getCollection =<< (Just `liftM` (T.pack `liftM` (lift getTicketFieldsUrl)))
diff --git a/src/Zendesk/Common.hs b/src/Zendesk/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Zendesk/Common.hs
@@ -0,0 +1,243 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverlappingInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module Zendesk.Common
+where
+
+import Control.Applicative ((<$>), (<*>))
+
+import Control.Exception (catches, SomeException(..), Handler(..))
+
+import Control.Failure (Failure(..))
+
+import Control.Monad (liftM, forM)
+
+import Control.Monad.Error (ErrorT(..), MonadError(..), Error(..))
+import Control.Monad.IO.Class (MonadIO(..))
+import Control.Monad.Logger (MonadLogger(..), logDebug)
+import Control.Monad.Reader (ReaderT (..), asks)
+import Control.Monad.Trans (lift)
+
+import Data.Aeson as J (eitherDecode, FromJSON(..), (.:), Value(..), Object)
+
+import Data.ByteString.Char8 as BS8 (pack, unpack)
+import Data.ByteString.Lazy (ByteString)
+
+import Data.Conduit (Source, yield)
+
+import Data.Default (Default(..))
+
+import Data.Text as T (pack, Text, unpack)
+
+import Data.X509 (HashALG(..))
+import Data.X509.CertificateStore (CertificateStore)
+import Data.X509.Validation (ValidationChecks(..), validate, defaultHooks, defaultChecks)
+
+import Json (deriveJSON)
+
+import Network.Connection (TLSSettings(..))
+
+import Network.HTTP.Client (getUri, applyBasicAuth)
+import Network.HTTP.Client.TLS (mkManagerSettings)
+import Network.HTTP.Conduit as HTTP ( httpLbs, parseUrl, withManagerSettings
+                                    , HttpException(..)
+                                    , Request(..), Response(..)
+                                    , responseBody, requestBody
+                                    , requestHeaders, checkStatus)
+import Network.HTTP.Types.Status (Status(..))
+
+import Network.TLS ( Credential, ClientHooks(..), ClientParams(..)
+                   , Credentials(..), Supported(..), defaultParamsClient
+                   , Shared(..))
+import Network.TLS.Extra.Cipher (ciphersuite_strong)
+
+data ZendeskError = TimeoutError
+                  | NoResourceError
+                  | BadRequestError String
+                  | NoFreeServersError String
+                  | InternalError String
+                  | UnknownError String
+
+instance Error ZendeskError where
+  strMsg = UnknownError
+
+instance Show ZendeskError where
+  show NoResourceError   = "Resource not found in datacenter"
+  show TimeoutError      = "Datacenter unavailable"
+  show (BadRequestError s)      = "Bad request: " ++ s
+  show (NoFreeServersError s) = "Not enough free servers (" ++ s ++ ")"
+  show (InternalError s) = "Internal error: " ++ s
+  show (UnknownError s)  = s
+
+runZendeskT :: ZendeskConfig -> ZendeskT m a -> m (Either ZendeskError a)
+runZendeskT c f = runErrorT $ runReaderT f c
+
+type ZendeskT m = ReaderT ZendeskConfig (ErrorT ZendeskError m)
+
+data ZendeskConfig = ZendeskConfig
+  { zendeskUrl :: String -- zendesk domain url
+  , zendeskUsername :: String
+  , zendeskPassword :: String
+  , zendeskTLS :: Maybe ZendeskTLSConfig
+  }
+
+data ZendeskTLSConfig = ZendeskTLSConfig
+  { tlsCredential :: Credential
+  , tlsCertificateStore :: CertificateStore
+  }
+
+class CollectionKey e where
+  collectionKey :: e -> Text
+
+data Collection e = Collection
+  { collectionElements :: [e]
+  , collectionCount    :: Int
+  , collectionNextPage :: Maybe Text
+  , collectionPrevPage :: Maybe Text
+  } deriving Show
+
+instance (CollectionKey e, FromJSON e) => FromJSON (Collection e) where
+  parseJSON (Object v) =
+    Collection
+          <$> v .: (collectionKey (undefined :: e))
+          <*> v .: "count"
+          <*> v .: "next_page"
+          <*> v .: "previous_page"
+
+showRequest :: Request -> String
+showRequest x = unlines
+  [ "Request {"
+  , "  method = " ++ show (method x)
+  , "  url = \"" ++ show (getUri x) ++ "\""
+  , "  headers = " ++ show (requestHeaders x)
+  -- , "  body = " ++ show (requestBody x)
+  , "}"
+  ]
+
+showResponse :: Response ByteString -> String
+showResponse x = unlines
+  [ "Response {"
+  , "  status = " ++ show (statusCode $ responseStatus x)
+  , "  headers = " ++ show (responseHeaders x)
+  , "  body = " ++ show (responseBody x)
+  , "}"
+  ]
+
+errorResponseText :: HTTP.Response ByteString -> String
+errorResponseText response =
+  either (\_ -> show $ responseBody response) errorReportError $ eitherDecode . responseBody $ response
+
+handleExceptionsAndResult :: MonadIO m => IO a -> ZendeskT m a
+handleExceptionsAndResult monad = do
+  res <- liftIO $ (fmap Right monad) `catches` [ Handler handleHttpException
+                                               , Handler handleOtherExceptions
+                                               ]
+  case res of
+    Left v -> throwError v
+    Right x -> return x
+
+decodeResponseBody :: (Monad m, ParseResponseBody a)
+                   => Response ByteString -> m (Either ZendeskError a)
+decodeResponseBody response
+  | statusCode (responseStatus response) `elem` [200..299] =
+    case parseBody . responseBody $ response of
+      Left e -> return $ Left $ InternalError $ "Json parse error: " ++ e
+      Right x -> return $ Right x
+  | statusCode (responseStatus response) == 404 = return $ Left NoResourceError
+  | statusCode (responseStatus response) == 400 = return $ Left $ BadRequestError $ errorResponseText response
+  | statusCode (responseStatus response) == 402 = return $ Left $ NoFreeServersError $ errorResponseText response
+  | statusCode (responseStatus response) == 502 = return $ Left $ TimeoutError
+  | statusCode (responseStatus response) `elem` [500..599] = return $ Left $ InternalError $ errorResponseText response
+  | otherwise = return $ Left $ UnknownError $ errorResponseText response
+
+handleHttpException :: HttpException -> IO (Either ZendeskError a)
+handleHttpException ResponseTimeout = return $ Left TimeoutError
+handleHttpException e               = return $ Left $ UnknownError $ show e
+
+handleOtherExceptions :: SomeException -> IO (Either ZendeskError a)
+handleOtherExceptions e = return $ Left $ UnknownError $ show e
+
+instance Monad m => Failure HttpException (ZendeskT m) where
+  failure e = throwError $ InternalError $ show e
+
+class Monad m => MonadZendesk m where
+  runZendesk :: ZendeskT m a -> m (Either ZendeskError a)
+
+runRequest :: (MonadIO m, MonadLogger m, ParseResponseBody a)
+           => Request -> ZendeskT m a
+runRequest request = do
+  username <- BS8.pack `liftM` (asks zendeskUsername)
+  password <- BS8.pack `liftM` (asks zendeskPassword)
+  let request' = applyBasicAuth username password $ request { checkStatus = \_ _ _ -> Nothing }
+
+  $logDebug $ T.pack $ "Sending request to zendesk: " ++ (showRequest request')
+
+  mTLSConfig <- asks zendeskTLS
+  let tlsSettings = case mTLSConfig of
+                      Nothing -> def
+                      Just tlsConfig ->
+                        let params = defaultParamsClient (BS8.unpack $ host request') ""
+                            validationChecks = defaultChecks
+                                                 { checkCAConstraints = False
+                                                 , checkLeafV3 = False
+                                                 , checkFQHN = False
+                                                 }
+                         in TLSSettings $ params
+                              { clientShared = (clientShared params)
+                                                 { sharedCredentials = Credentials [tlsCredential tlsConfig]
+                                                 , sharedCAStore = tlsCertificateStore tlsConfig
+                                                 }
+                              , clientHooks = def { onCertificateRequest = \_ -> return . Just $ tlsCredential tlsConfig
+                                                  , onServerCertificate = validate HashSHA256 defaultHooks validationChecks
+                                                  }
+                              , clientSupported = (clientSupported params)
+                                                    { supportedCiphers = ciphersuite_strong }
+                              }
+
+
+  let httpSettings = mkManagerSettings tlsSettings Nothing
+
+  response <- handleExceptionsAndResult $ do
+    withManagerSettings httpSettings $ httpLbs request'
+
+  $logDebug $ T.pack $ "Got response from zendesk: " ++ (showResponse response)
+
+  result <- decodeResponseBody response
+  case result of
+    Left v -> throwError v
+    Right x -> return x
+
+runRequestTo :: (MonadIO m, MonadLogger m, ParseResponseBody a)
+             => String -> ZendeskT m a
+runRequestTo url = parseUrl url >>= runRequest
+
+getCollection :: (CollectionKey e, MonadIO m, MonadLogger m, FromJSON e)
+              => Maybe Text -> Source (ZendeskT m) e
+getCollection Nothing = return ()
+getCollection (Just url) = do
+  collection <- lift $ runRequestTo $ T.unpack url
+  forM (collectionElements collection) yield
+  getCollection $ collectionNextPage collection
+
+data None = None
+  deriving (Show, Eq)
+
+class ParseResponseBody a where
+  parseBody :: ByteString -> Either String a
+
+instance ParseResponseBody None where
+  parseBody _ = Right $ None
+
+instance FromJSON a => ParseResponseBody a where
+  parseBody = eitherDecode
+
+data ErrorReport = ErrorReport { errorReportError :: String }
+  deriving Show
+
+deriveJSON ''ErrorReport
diff --git a/src/Zendesk/User.hs b/src/Zendesk/User.hs
new file mode 100644
--- /dev/null
+++ b/src/Zendesk/User.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Zendesk.User
+( createUser
+, getUsers
+, User(..)
+) where
+
+import Control.Monad (liftM)
+import Control.Monad.IO.Class (MonadIO(..))
+import Control.Monad.Logger (MonadLogger(..))
+import Control.Monad.Reader (asks)
+import Control.Monad.Trans (lift)
+import Data.Aeson as J (encode, object, (.=), Value(..), Object)
+import qualified Data.ByteString.Lazy as LBS (toStrict)
+import Data.CaseInsensitive (mk)
+import Data.Conduit (Source)
+import Data.Text as T (pack, Text)
+import Data.Time.Clock (UTCTime(..))
+
+import Json (deriveJSON, deriveJSON_, deriveEnumJSON)
+import Network.HTTP.Conduit as HTTP ( parseUrl
+                                    , RequestBody(..), requestBody
+                                    , method, requestHeaders)
+
+import Zendesk.Common
+
+data User = User
+  { userId              :: Maybe Int
+  , userUrl             :: Maybe Text
+  , userName            :: Text
+  , userCreatedAt       :: Maybe UTCTime
+  , userUpdatedAt       :: Maybe UTCTime
+  , userTimeZone        :: Maybe Text
+  , userEmail           :: Text
+  , userPhone           :: Maybe Text
+  , userLocale          :: Maybe Text
+  , userLocaleId        :: Maybe Int
+  , userOrganizationId  :: Maybe Int
+  , userRole            :: Text
+  , userVerified        :: Maybe Bool
+  , userPhoto           :: Maybe Attachment
+  } deriving (Show)
+
+data Attachment = Attachment
+  { attachmentId          :: Int
+  , attachmentFileName    :: Text
+  , attachmentContentUrl  :: Text
+  , attachmentContentType :: Text
+  , attachmentSize        :: Int
+  , attachmentThumbnails  :: Maybe [Attachment]
+  } deriving (Show)
+
+data UserReply = UserReply
+  { userReplyUser :: User
+  }
+
+newtype Name = Name String
+
+newtype Email = Email String
+
+createUser :: (MonadIO m, MonadLogger m) => Name -> Email -> ZendeskT m User
+createUser (Name name) (Email email) = do
+  initRequest <- parseUrl =<< getUsersUrl
+  let request = initRequest
+                { method = "POST"
+                , requestHeaders = [ (mk "Content-Type", "application/json") ]
+                , requestBody = RequestBodyBS $ LBS.toStrict $ encode
+                                  $ object ["user" .= CreateUserRequest name email]
+                }
+  userReplyUser `liftM` (runRequest request)
+
+getUsers :: (MonadIO m, MonadLogger m) => Source (ZendeskT m) User
+getUsers =
+  getCollection =<< (Just `liftM` (T.pack `liftM` (lift getUsersUrl)))
+
+getUsersUrl :: Monad m => ZendeskT m String
+getUsersUrl = do
+  baseUrl <- asks zendeskUrl
+  return $ baseUrl ++ "/api/v2/users.json"
+
+data CreateUserRequest = CreateUserRequest
+  { createUserRequestName  :: String
+  , createUserRequestEmail :: String
+  } deriving (Show)
+
+instance CollectionKey User where
+  collectionKey _ = "users"
+
+deriveJSON ''CreateUserRequest
+deriveJSON ''User
+deriveJSON ''UserReply
+deriveJSON ''Attachment
diff --git a/zendesk-api.cabal b/zendesk-api.cabal
new file mode 100644
--- /dev/null
+++ b/zendesk-api.cabal
@@ -0,0 +1,51 @@
+name:                zendesk-api
+version:             0.1.0.0
+synopsis:            Zendesk API for Haskell programming language.
+description:         This is a library for accessing zendesk api using haskell bindings.
+homepage:            https://github.com/VictorDenisov/zendesk-api
+license:             MIT
+license-file:        LICENSE
+author:              Victor Denisov
+maintainer:          denisovenator@gmail.com
+-- copyright:
+category:            API
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Zendesk
+  other-modules:       Json
+                     , Zendesk.Common
+                     , Zendesk.User
+  -- other-extensions:
+  build-depends:       aeson == 0.7.*
+                     , base >=4.5 && <4.7
+                     , bytestring == 0.10.0.2
+                     , case-insensitive == 1.1.0.3
+                     , conduit == 1.0.*
+                     , connection == 0.2.*
+                     , data-default == 0.5.*
+                     , failure == 0.2.*
+                     , http-client == 0.2.*
+                     , http-client-tls == 0.2.*
+                     , http-conduit == 2.0.0.3
+                     , http-types == 0.8.3
+                     , monad-logger == 0.3.*
+                     , mtl == 2.1.2
+                     , pem == 0.2.*
+                     , text >= 0.11.3
+                     , template-haskell == 2.8.0.0
+                     , time == 1.4.0.1
+                     , tls == 1.2.*
+                     , transformers == 0.3.0.0
+                     , unordered-containers == 0.2.*
+                     , x509 == 1.4.*
+                     , x509-store == 1.4.*
+                     , x509-validation == 1.5.*
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+source-repository head
+  type:         git
+  location:     https://github.com/VictorDenisov/zendesk-api
