diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2016, Plow Technologies
+
+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 copyright holder 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 THE COPYRIGHT
+OWNER OR CONTRIBUTORS 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/quickbooks.cabal b/quickbooks.cabal
new file mode 100644
--- /dev/null
+++ b/quickbooks.cabal
@@ -0,0 +1,102 @@
+name:                quickbooks
+version:             0.5.0.1
+synopsis:            QuickBooks API binding.
+license:             BSD3
+license-file:        LICENSE
+author:              Stack Builders
+maintainer:          hackage@stackbuilders.com
+copyright:           Plow Technologies
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.10
+
+description: This package is a binding to the QuickBooks API. The
+             documentation can be test using the `cabal test doctests`
+             command. However, the documentation tests are run against
+             an Intuit sandbox which you must create.
+
+             .
+
+             Visit Intuit's developer site to create an "app":
+             <https://developer.intuit.com/>
+
+             .
+
+             After creating an app you can use your "app token" and
+             consumer information to aquire credentials here:
+             <https://appcenter.intuit.com/Playground/OAuth>
+
+             .
+
+             This package is configure by environment variables. Once
+             you have your credentials please export the following
+             environment:
+             .
+             * INTUIT_COMPANY_ID (your app's company ID)
+             .
+             * INTUIT_CONSUMER_KEY (your app's consumer key)
+             .
+             * INTUIT_CONSUMER_SECRET (your app's consumer secret)
+             .
+             * INTUIT_TOKEN (the OAuth1.0 Token you created above *used for tests*)
+             .
+             * INTUIT_SECRET (the OAuth1.0 Secret you created above *used for tests*)
+             .
+             * INTUIT_HOSTNAME (which will be
+               sandbox-quickbooks.api.intuit.com for development/test
+               environments. See the developer website for production)
+
+             .
+
+             You can now run verify the documentation using  `cabal test`.
+
+source-repository head
+  type:     git
+  location: https://github.com/stackbuilders/quickbooks
+
+library
+  exposed-modules:     QuickBooks
+                       QuickBooks.Authentication
+                       QuickBooks.Customer
+                       QuickBooks.Invoice
+                       QuickBooks.Item
+                       QuickBooks.Logging
+                       QuickBooks.Types
+
+
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  build-depends:         base >= 4.7   && < 5
+                         -- We restrict the version of aeson until
+                         -- https://github.com/bos/aeson/issues/293
+                         -- is fixed.
+                       , aeson <= 0.9.0.1
+                       , authenticate-oauth
+                       , bytestring
+                       , http-client
+                       , http-types
+                       , http-client-tls
+                       , interpolate
+                       , text
+                       , fast-logger
+                       , thyme
+                       , old-locale
+                       , email-validate
+                       , yaml
+
+test-suite doctests
+  type:             exitcode-stdio-1.0
+  default-language: Haskell2010
+  ghc-options:      -Wall -threaded
+  hs-source-dirs:   src, test
+  main-is:          doctests.hs
+
+  other-modules:   Data
+
+  build-depends:
+    base >= 4.7 && < 5,
+    doctest
+
+  buildable:
+    False
diff --git a/src/QuickBooks.hs b/src/QuickBooks.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks.hs
@@ -0,0 +1,419 @@
+{-# LANGUAGE ImplicitParams    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+------------------------------------------------------------------------------
+-- |
+-- Module: QuickBooks
+--
+-- For more information, see:
+--
+--   * QuickBooks API Reference:
+--     <https://developer.intuit.com/docs/api/accounting>.
+--
+------------------------------------------------------------------------------
+
+module QuickBooks
+  ( -- * Authentication and authorization
+    getAccessTokens
+  , getAccessTokens'
+  , getTempTokens
+  , getTempTokens'
+  , authorizationURLForToken
+  , cancelOAuthAuthorization
+  , cancelOAuthAuthorization'
+    -- * Transaction entities
+    -- ** Invoices
+    -- *** Types
+  , module QuickBooks.Types
+    -- *** CRUD an invoice
+  , createInvoice
+  , createInvoice'
+  , readInvoice
+  , readInvoice'
+  , updateInvoice
+  , updateInvoice'
+  , deleteInvoice
+  , deleteInvoice'
+    -- *** Send an invoice via email
+  , EmailAddress
+  , emailAddress
+  , sendInvoice
+  , sendInvoice'
+    -- *** Read in configuration files
+  , readAPIConfigFromFile
+  , readAppConfigFromFile
+    -- * Name list entities
+    -- ** Customer
+  , queryCustomer
+  , queryCustomer'
+    -- ** Line
+  , queryItem
+  , queryItem'
+  ) where
+
+import QuickBooks.Authentication
+import QuickBooks.Types hiding (EmailAddress,emailAddress)
+
+import Control.Applicative     ((<$>),(<*>), (<|>))
+import Control.Arrow           (second)
+import Data.ByteString.Char8   (pack)
+import Data.Maybe              (fromJust)
+import Data.Text               (Text)
+import Network.HTTP.Client.TLS (tlsManagerSettings)
+import Network.HTTP.Client     (newManager)
+import System.Environment      (getEnvironment)
+import Text.Email.Validate     (EmailAddress, emailAddress)
+
+
+import QuickBooks.Customer
+import QuickBooks.Invoice      ( createInvoiceRequest
+                               , deleteInvoiceRequest
+                               , readInvoiceRequest
+                               , updateInvoiceRequest
+                               , sendInvoiceRequest
+                               )
+import QuickBooks.Item
+import QuickBooks.Logging      (apiLogger, getLogger)
+import Data.Yaml (ParseException, decodeFileEither)
+
+-- $setup
+--
+-- >>> import Data
+--
+-- >>> :set -XOverloadedStrings
+-- >>> maybeTestOAuthToken <- lookupTestOAuthTokenFromEnv
+-- >>> let oAuthToken = maybe (error "") id maybeTestOAuthToken
+
+-- |
+--
+-- >>> :{
+--   do eitherQueryCustomer <-
+--        queryCustomer oAuthToken "Rondonuwu Fruit and Vegi"
+--      case eitherQueryCustomer of
+--        Right (QuickBooksCustomerResponse (customer:_)) ->
+--          print (customerId customer)
+--        _ ->
+--          putStrLn "Nothing"
+-- :}
+-- Just "21"
+
+queryCustomer
+  :: OAuthToken
+  -> Text
+  -> IO (Either String (QuickBooksResponse [Customer]))
+queryCustomer tok =
+  queryQuickBooks tok . QueryCustomer
+
+queryCustomer'
+  :: APIConfig
+  -> AppConfig
+  -> OAuthToken
+  -> Text
+  -> IO (Either String (QuickBooksResponse [Customer]))
+queryCustomer' apiConfig appConfig tok =
+  queryQuickBooks' apiConfig appConfig tok . QueryCustomer
+
+-- |
+--
+-- >>> :{
+--   do eitherQueryItem <- queryItem oAuthToken "Hours"
+--      case eitherQueryItem of
+--        Right (QuickBooksItemResponse (item:_)) ->
+--          print (itemId item)
+--        _ ->
+--          putStrLn "Nothing"
+-- :}
+-- Just "2"
+
+queryItem
+  :: OAuthToken
+  -> Text
+  -> IO (Either String (QuickBooksResponse [Item]))
+queryItem tok =
+  queryQuickBooks tok . QueryItem
+
+queryItem'
+  :: APIConfig
+  -> AppConfig
+  -> OAuthToken
+  -> Text
+  -> IO (Either String (QuickBooksResponse [Item]))
+queryItem' apiConfig appConfig tok =
+  queryQuickBooks' apiConfig appConfig tok . QueryItem
+
+-- | Create an invoice.
+--
+-- Example:
+--
+-- >>> import Data.Maybe (fromJust)
+-- >>> :{
+-- do resp <- createInvoice oAuthToken testInvoice
+--    case resp of
+--      Left err -> putStrLn $ "My custom error message: " ++ err
+--      Right (QuickBooksInvoiceResponse invoice) -> do
+--        deleteInvoice oAuthToken (fromJust (invoiceId invoice)) (fromJust (invoiceSyncToken invoice))
+--        putStrLn "I created an invoice!"
+-- :}
+-- I created an invoice!
+--
+-- Note that we deleted the item we created using 'deleteInvoice'.
+
+createInvoice :: OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
+createInvoice tok = queryQuickBooks tok . CreateInvoice
+
+-- | Like createInvoice but accepts an APIConfig rather than reading it from the environment
+createInvoice' :: APIConfig -> AppConfig -> OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
+createInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . CreateInvoice
+
+
+-- | Retrieve the details of an invoice that has been previously created.
+--
+-- Example:
+--
+-- First, we create an invoice (see 'createInvoice'):
+--
+-- >>> import Data.Maybe (fromJust)
+-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
+--
+-- Then, we read the invoice and test that it is the same invoice we created:
+--
+-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
+-- >>> :{
+-- do eitherReadInvoice <- readInvoice oAuthToken cInvoiceId
+--    case eitherReadInvoice of
+--      Left _ -> return False
+--      Right (QuickBooksInvoiceResponse rInvoice) -> return (cInvoice == rInvoice)
+-- :}
+-- True
+--
+-- Finally, we delete the invoice we created:
+--
+-- >>> deleteInvoice oAuthToken cInvoiceId (fromJust (invoiceSyncToken cInvoice))
+
+readInvoice ::  OAuthToken -> InvoiceId -> IO (Either String (QuickBooksResponse Invoice))
+readInvoice tok = queryQuickBooks tok . ReadInvoice
+
+-- | Like readInvoice but accepts an APIConfig rather than reading it from the environment
+readInvoice' :: APIConfig -> AppConfig -> OAuthToken -> InvoiceId -> IO (Either String (QuickBooksResponse Invoice))
+readInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . ReadInvoice
+
+-- | Update an invoice.
+--
+-- Example:
+--
+-- First, we create an invoice (see 'createInvoice'):
+--
+-- >>> import Data.Maybe (fromJust)
+-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
+--
+-- Then, we update the customer reference of the invoice:
+--
+-- >>> let nInvoice = cInvoice { invoiceCustomerRef = Reference Nothing Nothing "1" }
+-- >>> :{
+-- do eitherUpdateInvoice <- updateInvoice oAuthToken nInvoice
+--    case eitherUpdateInvoice of
+--      Left _ -> return False
+--      Right (QuickBooksInvoiceResponse uInvoice) ->
+--        return (invoiceCustomerRef cInvoice == invoiceCustomerRef uInvoice)
+-- :}
+-- False
+--
+-- Finally, we delete the invoice we created:
+--
+-- >>> deleteInvoice oAuthToken (fromJust (invoiceId cInvoice)) (fromJust (invoiceSyncToken cInvoice))
+
+updateInvoice ::  OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
+updateInvoice tok = queryQuickBooks tok . UpdateInvoice
+
+-- | Like updateInvoice but accepts an APIConfig rather than reading it from the environment
+updateInvoice' :: APIConfig -> AppConfig -> OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
+updateInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . UpdateInvoice
+
+-- | Delete an invoice.
+--
+-- Example:
+--
+-- First, we create an invoice (see 'createInvoice'):
+--
+-- >>> import Data.Maybe (fromJust)
+-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
+--
+-- Then, we delete it:
+--
+-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
+-- >>> let cInvoiceSyncToken = fromJust (invoiceSyncToken cInvoice)
+-- >>> :{
+-- do eitherDeleteInvoice <- deleteInvoice oAuthToken cInvoiceId cInvoiceSyncToken
+--    case eitherDeleteInvoice of
+--      Left e -> putStrLn e
+--      Right _ -> putStrLn "I deleted an invoice!"
+-- :}
+-- I deleted an invoice!
+
+deleteInvoice ::  OAuthToken -> InvoiceId -> SyncToken -> IO (Either String (QuickBooksResponse DeletedInvoice))
+deleteInvoice tok iId = queryQuickBooks tok . DeleteInvoice iId
+
+-- | Like deleteInvoice but accepts an APIConfig rather than reading it from the environment
+deleteInvoice' :: APIConfig -> AppConfig -> OAuthToken -> InvoiceId -> SyncToken -> IO (Either String (QuickBooksResponse DeletedInvoice))
+deleteInvoice' apiConfig appConfig tok iId = queryQuickBooks' apiConfig appConfig tok . DeleteInvoice iId
+
+
+-- | Send an invoice via email.
+--
+-- Example:
+--
+-- First, we create an invoice (see 'createInvoice'):
+--
+-- >>> import Data.Maybe (fromJust)
+-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
+--
+-- Then, we send the invoice via email:
+--
+-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
+-- >>> let testEmail = fromJust (emailAddress "test@test.com")
+-- >>> :{
+-- do eitherSendInvoice <- sendInvoice oAuthToken cInvoiceId testEmail
+--    case eitherSendInvoice of
+--      Left e  -> putStrLn e
+--      Right _ -> putStrLn "I sent an invoice!"
+-- :}
+-- I sent an invoice!
+--
+-- Finally, we delete the invoice we created:
+--
+-- >>> deleteInvoice oAuthToken cInvoiceId (fromJust (invoiceSyncToken cInvoice))
+
+sendInvoice ::  OAuthToken -> InvoiceId -> EmailAddress -> IO (Either String (QuickBooksResponse Invoice))
+sendInvoice tok invId = queryQuickBooks tok . SendInvoice invId
+
+sendInvoice' :: APIConfig
+             -> AppConfig
+             -> OAuthToken
+             -> InvoiceId
+             -> EmailAddress
+             -> IO (Either String (QuickBooksResponse Invoice))
+sendInvoice' apiConfig appConfig tok invId  =
+  queryQuickBooks' apiConfig appConfig tok . SendInvoice invId
+
+-- | Get temporary tokens to request permission.
+--
+-- Example:
+--
+-- >>> :{
+-- do eitherTempTokens <- getTempTokens "localhost"
+--    case eitherTempTokens of
+--      Left e -> putStrLn e
+--      Right _ -> putStrLn "I got my request tokens!"
+-- :}
+-- ...
+-- I got my request tokens!
+
+getTempTokens :: CallbackURL -> IO (Either String (QuickBooksResponse OAuthToken))
+getTempTokens =
+  queryQuickBooksOAuth Nothing . GetTempOAuthCredentials
+
+getTempTokens' :: AppConfig -> CallbackURL -> IO (Either String (QuickBooksResponse OAuthToken))
+getTempTokens' appConfig =
+  queryQuickBooksOAuth' appConfig Nothing . GetTempOAuthCredentials
+ 
+-- | Exchange oauth_verifier for access tokens
+getAccessTokens :: OAuthToken -> OAuthVerifier -> IO (Either String (QuickBooksResponse OAuthToken))
+getAccessTokens tempToken =
+  queryQuickBooksOAuth (Just tempToken) . GetAccessTokens
+
+getAccessTokens' :: AppConfig          -- Your application's consumer key and consumer secret
+                 -> OAuthToken         -- The temporary OAuth tokens obtained from getTempTokens
+                 -> OAuthVerifier      -- The OAuthVerifier returned by QuickBooks when it calls your callback
+                 -> IO (Either String (QuickBooksResponse OAuthToken))
+getAccessTokens' appConfig tempToken = do
+  queryQuickBooksOAuth' appConfig (Just tempToken) . GetAccessTokens
+
+-- | Invalidate an OAuth access token and disconnect from QuickBooks.
+cancelOAuthAuthorization :: OAuthToken -> IO (Either String (QuickBooksResponse ()))
+cancelOAuthAuthorization tok =
+  queryQuickBooksOAuth (Just tok)  DisconnectQuickBooks
+
+cancelOAuthAuthorization' :: AppConfig
+                          -> OAuthToken
+                          -> IO (Either String (QuickBooksResponse ()))
+cancelOAuthAuthorization' appConfig tok =
+  queryQuickBooksOAuth' appConfig (Just tok) DisconnectQuickBooks
+
+queryQuickBooks :: OAuthToken -> QuickBooksQuery a -> IO (Either String (QuickBooksResponse a))
+queryQuickBooks tok query = do
+  apiConfig <- readAPIConfig
+  appConfig <- readAppConfig
+  queryQuickBooks' apiConfig appConfig tok query
+
+queryQuickBooks' :: APIConfig -> AppConfig -> OAuthToken -> QuickBooksQuery a -> IO (Either String (QuickBooksResponse a))
+queryQuickBooks' apiConfig appConfig tok query = do
+  manager   <- newManager tlsManagerSettings
+  logger    <- getLogger apiLogger
+  let ?appConfig = appConfig
+  let ?apiConfig = apiConfig
+  let ?manager   = manager
+  let ?logger    = logger
+  case query of
+    CreateInvoice invoice               -> createInvoiceRequest tok invoice
+    ReadInvoice _invoiceId              -> readInvoiceRequest tok _invoiceId
+    UpdateInvoice invoice               -> updateInvoiceRequest tok invoice
+    DeleteInvoice _invoiceId syncToken  -> deleteInvoiceRequest tok _invoiceId syncToken
+    SendInvoice _invoiceId emailAddr    -> sendInvoiceRequest tok _invoiceId emailAddr    
+    QueryCustomer queryCustomerName     -> queryCustomerRequest tok queryCustomerName
+    QueryItem queryItemName             -> queryItemRequest tok queryItemName
+
+queryQuickBooksOAuth :: Maybe OAuthToken
+                     -> QuickBooksOAuthQuery a
+                     -> IO (Either String (QuickBooksResponse a))
+queryQuickBooksOAuth maybeOAuthToken query = do
+  appConfig <- readAppConfig
+  queryQuickBooksOAuth' appConfig maybeOAuthToken query 
+
+queryQuickBooksOAuth' :: AppConfig
+                      -> Maybe OAuthToken
+                      -> QuickBooksOAuthQuery a
+                      -> IO (Either String (QuickBooksResponse a))
+queryQuickBooksOAuth' appConfig maybeOauthToken query = do
+  manager   <- newManager tlsManagerSettings
+  logger    <- getLogger apiLogger
+  let ?appConfig = appConfig
+  let ?manager   = manager
+  let ?logger    = logger
+  case query of
+    (GetTempOAuthCredentials callbackURL) -> getTempOAuthCredentialsRequest callbackURL
+    (GetAccessTokens oauthVerifier)       -> getAccessTokensRequest (fromJust maybeOauthToken) oauthVerifier
+    DisconnectQuickBooks                  -> disconnectRequest (fromJust maybeOauthToken)
+
+readAPIConfig :: IO APIConfig
+readAPIConfig = do
+  env <- getEnvironment
+  case lookupAPIConfig env of
+    Just config -> return config
+    Nothing     -> fail "The environment variables INTUIT_COMPANY_ID,INTUIT_TOKEN,INTUIT_SECRET, and INTUIT_HOSTNAME must be set"
+
+readAppConfig :: IO AppConfig
+readAppConfig = do
+  env <- getEnvironment
+  case lookupAppConfig env of
+    Just config -> return config
+    Nothing     -> fail "The evironment variables INTUIT_CONSUMER_KEY and INTUIT_CONSUMER_SECRET must be set"
+
+lookupAPIConfig :: [(String, String)] -> Maybe APIConfig
+lookupAPIConfig environment = APIConfig <$> lookup "INTUIT_COMPANY_ID" env
+                                        <*> lookup "INTUIT_TOKEN" env
+                                        <*> lookup "INTUIT_SECRET" env
+                                        <*> lookup "INTUIT_HOSTNAME" env
+                                        <*> (lookup "INTUIT_API_LOGGING_ENABLED" env <|> Just "true")
+    where env = map (second pack) environment
+
+readAPIConfigFromFile :: FilePath -> IO (Either ParseException APIConfig)
+readAPIConfigFromFile = decodeFileEither
+
+readAppConfigFromFile :: FilePath -> IO (Either ParseException AppConfig)
+readAppConfigFromFile = decodeFileEither
+
+lookupAppConfig :: [(String, String)] -> Maybe AppConfig
+lookupAppConfig environment = AppConfig <$> lookup "INTUIT_CONSUMER_KEY" env
+                                        <*> lookup "INTUIT_CONSUMER_SECRET" env
+    where env = map (second pack) environment
diff --git a/src/QuickBooks/Authentication.hs b/src/QuickBooks/Authentication.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Authentication.hs
@@ -0,0 +1,130 @@
+{-# LANGUAGE ImplicitParams     #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE RankNTypes         #-}
+{-# LANGUAGE ConstraintKinds    #-}
+
+module QuickBooks.Authentication
+  ( getTempOAuthCredentialsRequest
+  , getAccessTokensRequest
+  , oauthSignRequest
+  , authorizationURLForToken
+  , disconnectRequest
+  ) where
+
+import Control.Monad (void, liftM, ap)
+import Data.Monoid ((<>))
+import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Char8 (unpack, ByteString)
+import Network.HTTP.Client (Manager
+                           ,Request(..)
+                           ,RequestBody(RequestBodyLBS)
+                           ,responseBody
+                           ,parseUrl
+                           ,httpLbs)
+import Network.HTTP.Types.URI (parseSimpleQuery)
+import Web.Authenticate.OAuth (signOAuth
+                              ,newCredential
+                              ,emptyCredential
+                              ,injectVerifier
+                              ,newOAuth
+                              ,OAuth(..))
+
+import QuickBooks.Logging (logAPICall')
+import QuickBooks.Types
+
+getTempOAuthCredentialsRequest :: AppEnv
+                               => CallbackURL
+                               -> IO (Either String (QuickBooksResponse OAuthToken)) -- ^ Temporary OAuthToken
+getTempOAuthCredentialsRequest callbackURL =
+  return.handleQuickBooksTokenResponse "Couldn't get temporary tokens" =<< tokensRequest
+    where tokensRequest = getTokens temporaryTokenURL "?oauth_callback=" callbackURL oauthSignRequestWithEmptyCredentials
+
+getAccessTokensRequest :: AppEnv
+                       => OAuthToken                                         -- ^ Temporary Token
+                       -> OAuthVerifier                                      -- ^ OAuthVerifier provided by QuickBooks
+                       -> IO (Either String (QuickBooksResponse OAuthToken)) -- ^ OAuthToken                                                                                 
+getAccessTokensRequest tempToken verifier =
+  return.handleQuickBooksTokenResponse "Couldn't get access tokens" =<< tokensRequest
+  where
+    tokensRequest = getTokens accessTokenURL "?oauth_token=" (unpack $ token tempToken)
+                                                             (oauthSignRequestWithVerifier verifier tempToken)
+                    
+disconnectRequest :: AppEnv
+                  => OAuthToken
+                  -> IO (Either String (QuickBooksResponse ()))
+disconnectRequest tok = do
+  req  <- parseUrl $ disconnectURL
+  req' <- oauthSignRequest tok req
+  void $ httpLbs req' ?manager
+  logAPICall' req'
+  return $ Right QuickBooksVoidResponse
+    
+getTokens :: AppEnv
+          => String                  -- ^ Endpoint to request the token
+          -> String                  -- ^ URL parameter name
+          -> String                  -- ^ URL parameter value
+          -> ((?appConfig :: AppConfig) => Request -> IO Request) -- ^ Signing function
+          -> IO (Maybe OAuthToken)
+getTokens tokenURL parameterName parameterValue signRequest = do
+  request  <- parseUrl $ concat [tokenURL, parameterName, parameterValue]
+  request' <- signRequest request { method="POST", requestBody = RequestBodyLBS "" }
+  response <- httpLbs request' ?manager
+  logAPICall' request'
+  return $ tokensFromResponse (responseBody response)
+
+oauthSignRequestWithVerifier :: (?appConfig :: AppConfig)
+                             => OAuthVerifier
+                             -> OAuthToken
+                             -> Request
+                             -> IO Request
+oauthSignRequestWithVerifier verifier tempTokens = signOAuth oauthApp credsWithVerifier
+  where
+    credentials       = newCredential (token tempTokens)
+                                      (tokenSecret tempTokens)
+    credsWithVerifier = injectVerifier (unOAuthVerifier verifier) credentials
+    oauthApp          = newOAuth { oauthConsumerKey    = consumerToken ?appConfig
+                                 , oauthConsumerSecret = consumerSecret ?appConfig }
+
+oauthSignRequest :: (?appConfig :: AppConfig)
+                 => OAuthToken
+                 -> Request
+                 -> IO Request
+oauthSignRequest tok req = signOAuth oauthApp credentials req
+  where
+    credentials = newCredential (token tok)
+                                (tokenSecret tok)
+    oauthApp    = newOAuth { oauthConsumerKey    = consumerToken ?appConfig
+                           , oauthConsumerSecret = consumerSecret ?appConfig }
+
+oauthSignRequestWithEmptyCredentials :: (?appConfig :: AppConfig)
+                                     => Request
+                                     -> IO Request
+oauthSignRequestWithEmptyCredentials = signOAuth oauthApp credentials
+  where
+    credentials = emptyCredential
+    oauthApp    = newOAuth { oauthConsumerKey    = consumerToken ?appConfig
+                           , oauthConsumerSecret = consumerSecret ?appConfig }
+
+disconnectURL :: String
+disconnectURL = "https://appcenter.intuit.com/api/v1/connection/disconnect"
+
+accessTokenURL :: String
+accessTokenURL = "https://oauth.intuit.com/oauth/v1/get_access_token"
+
+temporaryTokenURL :: String
+temporaryTokenURL = "https://oauth.intuit.com/oauth/v1/get_request_token"
+
+authorizationURL :: ByteString
+authorizationURL = "https://appcenter.intuit.com/Connect/Begin"
+
+authorizationURLForToken :: OAuthToken -> ByteString 
+authorizationURLForToken oatoken = authorizationURL <> "?oauth_token=" <> (token oatoken)
+ 
+handleQuickBooksTokenResponse :: String -> Maybe OAuthToken -> Either String (QuickBooksResponse OAuthToken)
+handleQuickBooksTokenResponse _ (Just tokensInResponse) = Right $ QuickBooksAuthResponse tokensInResponse
+handleQuickBooksTokenResponse errorMessage Nothing      = Left errorMessage
+
+tokensFromResponse :: BSL.ByteString -> Maybe OAuthToken
+tokensFromResponse response = OAuthToken `liftM` lookup "oauth_token" responseParams
+                                         `ap` lookup "oauth_token_secret" responseParams
+  where responseParams = parseSimpleQuery (BSL.toStrict response)
diff --git a/src/QuickBooks/Customer.hs b/src/QuickBooks/Customer.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Customer.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE ImplicitParams    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE ConstraintKinds   #-}
+
+------------------------------------------------------------------------------
+-- |
+-- Module      : QuickBooks.Customer
+-- Description :
+-- Copyright   :
+-- License     :
+-- Maintainer  :
+-- Stability   :
+-- Portability :
+--
+--
+--
+------------------------------------------------------------------------------
+
+module QuickBooks.Customer
+  ( queryCustomerRequest
+  )
+  where
+
+import QuickBooks.Authentication
+import QuickBooks.Logging
+import QuickBooks.Types
+
+import qualified Data.Aeson as Aeson
+import Data.String.Interpolate (i)
+import Data.Text (Text)
+import Network.HTTP.Client
+import Network.HTTP.Types.Header (hAccept)
+
+-- GET /v3/company/<companyID>/query=<selectStatement>
+
+queryCustomerRequest :: APIEnv 
+                     => OAuthToken
+                     -> Text
+                     -> IO (Either String (QuickBooksResponse [Customer]))
+queryCustomerRequest tok queryCustomerName = do
+  let apiConfig = ?apiConfig
+  let queryURI = parseUrl [i|#{queryURITemplate apiConfig}#{query}|]
+  req <- oauthSignRequest tok =<< queryURI
+  let oauthHeaders = requestHeaders req
+  let req' = req { method = "GET"
+                 , requestHeaders = oauthHeaders ++ [(hAccept, "application/json")]
+                 }
+  resp <- httpLbs req' ?manager
+  logAPICall req'
+  let eitherAllCustomers = Aeson.eitherDecode (responseBody resp)
+  case eitherAllCustomers of
+    Left er -> return (Left er)
+    Right (QuickBooksCustomerResponse allCustomers) ->
+      return $ Right $ QuickBooksCustomerResponse $
+        filter (\Customer{..} -> customerDisplayName == queryCustomerName) allCustomers
+  where
+    query :: String
+    query = "SELECT * FROM Customer"
+
+queryURITemplate :: APIConfig -> String
+queryURITemplate APIConfig{..} =
+  [i|https://#{hostname}/v3/company/#{companyId}/query?query=|]
diff --git a/src/QuickBooks/Invoice.hs b/src/QuickBooks/Invoice.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Invoice.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE ImplicitParams    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE ConstraintKinds   #-}
+{-# LANGUAGE TypeOperators     #-}
+{-# LANGUAGE RankNTypes        #-}
+{-# LANGUAGE PolyKinds         #-}
+------------------------------------------------------------------------------
+-- |
+-- Module      : QuickBooks.Requests
+-- Description :
+-- Copyright   :
+-- License     :
+-- Maintainer  :
+-- Stability   :
+-- Portability :
+--
+--
+--
+------------------------------------------------------------------------------
+
+module QuickBooks.Invoice
+ ( createInvoiceRequest
+ , readInvoiceRequest
+ , updateInvoiceRequest
+ , deleteInvoiceRequest
+ , sendInvoiceRequest
+ ) where
+
+import Data.Aeson                (encode, eitherDecode, object, Value(String))
+import Data.String.Interpolate   (i)
+import Network.HTTP.Client       (httpLbs
+                                 ,parseUrl
+                                 ,Request(..)
+                                 ,RequestBody(..)
+                                 ,Response(responseBody))
+import Network.HTTP.Types.Header (hAccept,hContentType)
+
+import QuickBooks.Authentication (oauthSignRequest)
+
+import QuickBooks.Types (APIConfig(..)
+                        ,Invoice
+                        ,InvoiceId(..)
+                        ,QuickBooksResponse
+                        ,SyncToken(..)
+                        ,DeletedInvoice(..)
+                        ,OAuthToken
+                        ,APIEnv)
+
+import Text.Email.Validate (EmailAddress, toByteString)
+import QuickBooks.Logging  (logAPICall)
+
+-- | Create an invoice.
+createInvoiceRequest :: APIEnv
+                     => OAuthToken
+                     -> Invoice                        
+                     -> IO (Either String (QuickBooksResponse Invoice))
+createInvoiceRequest tok = postInvoice tok
+                           
+-- | Update an invoice.
+updateInvoiceRequest :: APIEnv
+                     => OAuthToken
+                     -> Invoice
+                     -> IO (Either String (QuickBooksResponse Invoice))
+updateInvoiceRequest tok = postInvoice tok
+
+-- | Read an invoice.
+readInvoiceRequest :: APIEnv
+                   => OAuthToken
+                   -> InvoiceId
+                   -> IO (Either String (QuickBooksResponse Invoice))
+readInvoiceRequest tok iId = do
+  let apiConfig = ?apiConfig
+  req  <- oauthSignRequest tok =<< parseUrl [i|#{invoiceURITemplate apiConfig}#{unInvoiceId iId}|]
+  let oauthHeaders = requestHeaders req
+  let req' = req{method = "GET", requestHeaders = oauthHeaders ++ [(hAccept, "application/json")]}
+  resp <-  httpLbs req' ?manager
+  logAPICall req'
+  return $ eitherDecode $ responseBody resp
+
+-- | Delete an invoice.
+deleteInvoiceRequest :: APIEnv
+                     => OAuthToken
+                     -> InvoiceId
+                     -> SyncToken
+                     -> IO (Either String (QuickBooksResponse DeletedInvoice))
+deleteInvoiceRequest tok iId syncToken = do
+  let apiConfig = ?apiConfig
+  req  <- parseUrl [i|#{invoiceURITemplate apiConfig}?operation=delete|]
+  req' <- oauthSignRequest tok req{ method = "POST"
+                                  , requestBody    = RequestBodyLBS $ encode body
+                                  , requestHeaders = [ (hAccept, "application/json")
+                                                     , (hContentType, "application/json")
+                                                     ]
+                                  }
+  resp <-  httpLbs req' ?manager
+  logAPICall req'
+  return $ eitherDecode $ responseBody resp
+  where
+    body = object [ ("Id", String (unInvoiceId iId))
+                  , ("SyncToken", String (unSyncToken syncToken))
+                  ]
+
+-- | email and invoice
+sendInvoiceRequest :: APIEnv
+                   => OAuthToken
+                   -> InvoiceId 
+                   -> EmailAddress 
+                   -> IO (Either String (QuickBooksResponse Invoice))
+sendInvoiceRequest tok iId emailAddr =  do
+  let apiConfig = ?apiConfig
+  req  <- parseUrl [i|#{invoiceURITemplate apiConfig}#{unInvoiceId iId}/send?sendTo=#{toByteString emailAddr}|]
+  req' <- oauthSignRequest tok req{ method = "POST"
+                                  , requestHeaders = [ (hAccept, "application/json")
+                                                     ]
+                                  }
+  logAPICall req'
+  resp <-  httpLbs req' ?manager
+  return $ eitherDecode $ responseBody resp
+
+invoiceURITemplate :: APIConfig -> String
+invoiceURITemplate APIConfig{..} = [i|https://#{hostname}/v3/company/#{companyId}/invoice/|]
+
+
+postInvoice :: APIEnv
+            => OAuthToken
+            -> Invoice
+            -> IO (Either String (QuickBooksResponse Invoice))
+postInvoice tok invoice = do
+  let apiConfig = ?apiConfig
+  req <- parseUrl [i|#{invoiceURITemplate apiConfig}|]
+  req' <- oauthSignRequest tok req{ method         = "POST"
+                                  , requestBody    = RequestBodyLBS $ encode invoice
+                                  , requestHeaders = [ (hAccept, "application/json")
+                                                     , (hContentType, "application/json")
+                                                     ]
+                                  } 
+  resp <- httpLbs req' ?manager
+  logAPICall req'
+  return $ eitherDecode $ responseBody resp
diff --git a/src/QuickBooks/Item.hs b/src/QuickBooks/Item.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Item.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE ImplicitParams    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE ConstraintKinds   #-}
+
+------------------------------------------------------------------------------
+-- |
+-- Module      : QuickBooks.Item
+-- Description :
+-- Copyright   :
+-- License     :
+-- Maintainer  :
+-- Stability   :
+-- Portability :
+--
+--
+--
+------------------------------------------------------------------------------
+
+module QuickBooks.Item
+  ( queryItemRequest
+  )
+  where
+
+import QuickBooks.Authentication
+import QuickBooks.Logging
+import QuickBooks.Types
+
+import qualified Data.Aeson as Aeson
+import Data.String.Interpolate (i)
+import Data.Text (Text)
+import Network.HTTP.Client
+import Network.HTTP.Types.Header (hAccept)
+
+-- GET /v3/company/<companyID>/query=<selectStatement>
+
+queryItemRequest :: APIEnv
+                 => OAuthToken
+                 -> Text
+                 -> IO (Either String (QuickBooksResponse [Item]))
+queryItemRequest tok queryItemName = do
+  let apiConfig = ?apiConfig
+  let queryURI = parseUrl [i|#{queryURITemplate apiConfig}#{query}|]
+  req <- oauthSignRequest tok =<< queryURI
+  let oauthHeaders = requestHeaders req
+  let req' = req { method = "GET"
+                 , requestHeaders = oauthHeaders ++ [(hAccept, "application/json")]
+                 }
+  resp <- httpLbs req' ?manager
+  logAPICall req'
+  let eitherAllItems = Aeson.eitherDecode (responseBody resp)
+  case eitherAllItems of
+    Left er -> return (Left er)
+    Right (QuickBooksItemResponse allItems) ->
+      return $ Right $ QuickBooksItemResponse $
+        filter (\Item{..} -> itemName == queryItemName) allItems
+  where
+    query :: String
+    query = "SELECT * FROM Item"
+
+queryURITemplate :: APIConfig -> String
+queryURITemplate APIConfig{..} =
+  [i|https://#{hostname}/v3/company/#{companyId}/query?query=|]
diff --git a/src/QuickBooks/Logging.hs b/src/QuickBooks/Logging.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Logging.hs
@@ -0,0 +1,55 @@
+{-# LANGUAGE ImplicitParams    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+module QuickBooks.Logging ( logAPICall
+                          , logAPICall'
+                          , Logger
+                          , apiLogger
+                          , getLogger
+                          ) where
+
+import Control.Monad             (when)
+import Data.Char                 (toLower)
+import Data.IORef
+import Data.Monoid               ((<>))
+import Data.String               (fromString)
+import Data.String.Interpolate   (i)
+import Data.Thyme
+import Network.HTTP.Client       (Request(..),RequestBody(..),getUri)
+import System.IO.Unsafe
+import System.Locale 
+import System.Log.FastLogger     (LogStr, pushLogStr, flushLogStr, newStdoutLoggerSet)
+import qualified Data.ByteString.Char8 as BS
+
+import QuickBooks.Types (APIConfig(..), Logger)
+
+apiLogger :: IORef Logger
+apiLogger = unsafePerformIO $ newIORef =<< newStdoutLoggerSet 0
+
+getLogger :: IORef Logger -> IO Logger
+getLogger = readIORef
+
+logAPICall :: ( ?logger :: Logger
+              , ?apiConfig :: APIConfig
+              ) => Request -> IO ()
+logAPICall req =
+  let isLoggingEnabled = BS.map toLower (loggingEnabled ?apiConfig)
+      in when (isLoggingEnabled == "true") $ logAPICall' req
+
+logAPICall' :: (?logger :: Logger) => Request -> IO ()
+logAPICall' req = do 
+  now <- getCurrentTime
+  let formattedTime = fromString $ formatTime defaultTimeLocale rfc822DateFormat now
+  pushLogStr ?logger (requestLogLine req formattedTime)
+  flushLogStr ?logger
+
+requestLogLine :: Request -> LogStr -> LogStr
+requestLogLine req formattedTime =
+  let body = case requestBody req of
+              (RequestBodyLBS bs) -> show bs 
+              (RequestBodyBS bs)  -> show bs
+              _                   -> ""
+  in formattedTime <> fromString [i| [INFO] [#{method req}] [#{getUri req}] [#{body}]\n|]
+
diff --git a/src/QuickBooks/Types.hs b/src/QuickBooks/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/QuickBooks/Types.hs
@@ -0,0 +1,721 @@
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ImplicitParams             #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE TemplateHaskell            #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE ConstraintKinds            #-}
+
+
+------------------------------------------------------------------------------
+-- |
+-- Module      : QuickBooks.Types
+-- Description :
+-- Copyright   :
+-- License     :
+-- Maintainer  :
+-- Stability   :
+-- Portability :
+--
+--
+--
+------------------------------------------------------------------------------
+
+module QuickBooks.Types where
+
+import           Control.Applicative ((<$>), (<*>))
+import           Control.Monad       (mzero)
+import           Data.Aeson          (FromJSON (..), ToJSON(..), Value (Object),
+                                      (.:), object, (.=))
+import           Data.Aeson.TH       (Options (fieldLabelModifier, omitNothingFields),
+                                      defaultOptions, deriveJSON)
+import           Data.ByteString     (ByteString)
+import           Data.Char           (toLower)
+import           Data.Text           (Text)
+import           Data.Text.Encoding  (encodeUtf8, decodeUtf8)
+import           Prelude             hiding (lines)
+import qualified Text.Email.Validate as E (EmailAddress)
+import           System.Log.FastLogger (LoggerSet)
+import           Network.HTTP.Client   (Manager)
+
+type Logger = LoggerSet
+
+type CallbackURL = String
+
+newtype OAuthVerifier = OAuthVerifier { unOAuthVerifier :: ByteString }
+  deriving (Show, Eq)
+
+-- | QuickBooks Application Keys
+
+data AppConfig = AppConfig
+  { consumerToken  :: !ByteString
+  , consumerSecret :: !ByteString
+  } deriving (Show, Eq)
+
+instance FromJSON AppConfig where
+  parseJSON (Object o) = AppConfig <$> (parseByteString o "consumerToken")
+                                   <*> (parseByteString o "consumerSecret")
+    where parseByteString obj name = encodeUtf8 <$> (obj .: name)
+  parseJSON _ = mzero
+  
+data APIConfig = APIConfig
+  { companyId      :: !ByteString
+  , oauthToken     :: !ByteString
+  , oauthSecret    :: !ByteString
+  , hostname       :: !ByteString
+  , loggingEnabled :: !ByteString
+  } deriving (Show, Eq)
+
+instance FromJSON APIConfig where
+  parseJSON (Object o) = APIConfig <$> (parseByteString o "companyId")
+                                   <*> (parseByteString o "oauthToken")
+                                   <*> (parseByteString o "oauthSecret")
+                                   <*> (parseByteString o "hostname")
+                                   <*> (parseByteString o "loggingEnabled")
+    where parseByteString obj name = encodeUtf8 <$> (obj .: name)
+  parseJSON _ = mzero
+
+instance ToJSON APIConfig where
+  toJSON (APIConfig cId oToken oSecret hName lEnabled) = object [
+                                                                  "companyId" .= (decodeUtf8 cId),
+                                                                  "oauthToken" .= (decodeUtf8 oToken),
+                                                                  "oauthSecret" .= (decodeUtf8 oSecret),
+                                                                  "hostname" .= (decodeUtf8 hName),
+                                                                  "loggingEnabled" .= (decodeUtf8 lEnabled)
+                                                                ]
+                                                                
+  
+type APIEnv = ( ?apiConfig :: APIConfig
+              , AppEnv               
+              , NetworkEnv              
+              )
+
+type AppEnv = ( ?appConfig :: AppConfig
+              , NetworkEnv   
+              )
+              
+type NetworkEnv = ( ?manager :: Manager
+                  , ?logger  :: Logger
+                  )
+             
+-- | A request or access OAuth token.
+
+data OAuthToken = OAuthToken
+  { token       :: ByteString
+  , tokenSecret :: ByteString
+  } deriving (Show, Eq)
+
+data family QuickBooksResponse a
+data instance QuickBooksResponse Invoice = QuickBooksInvoiceResponse { quickBooksResponseInvoice :: Invoice }
+data instance QuickBooksResponse DeletedInvoice = QuickBooksDeletedInvoiceResponse DeletedInvoice
+data instance QuickBooksResponse OAuthToken = QuickBooksAuthResponse { tokens :: OAuthToken }
+data instance QuickBooksResponse () = QuickBooksVoidResponse
+
+data instance QuickBooksResponse [Customer] =
+  QuickBooksCustomerResponse { quickBooksResponseCustomer :: [Customer] }
+
+data instance QuickBooksResponse [Item] =
+  QuickBooksItemResponse { quickBooksResponseItem :: [Item] }
+
+instance FromJSON (QuickBooksResponse Invoice) where
+  parseJSON (Object o) = QuickBooksInvoiceResponse `fmap` (o .: "Invoice")
+  parseJSON _          = fail "Could not parse invoice response from QuickBooks"
+
+instance FromJSON (QuickBooksResponse DeletedInvoice) where
+  parseJSON (Object o) = QuickBooksDeletedInvoiceResponse `fmap` (o .: "Invoice")
+  parseJSON _          = fail "Could not parse deleted invoice response from QuickBooks"
+
+instance FromJSON (QuickBooksResponse [Customer]) where
+  parseJSON (Object o) = do
+    let customers =
+          o .: "QueryResponse" >>= \queryResponse -> queryResponse .: "Customer"
+    fmap QuickBooksCustomerResponse customers
+  parseJSON _          = fail "Could not parse customer response from QuickBooks"
+
+instance FromJSON (QuickBooksResponse [Item]) where
+  parseJSON (Object o) = do
+    let items =
+          o .: "QueryResponse" >>= \queryResponse -> queryResponse .: "Item"
+    fmap QuickBooksItemResponse items
+  parseJSON _          = fail "Could not parse item response from QuickBooks"
+
+type QuickBooksQuery a = QuickBooksRequest (QuickBooksResponse a)
+type QuickBooksOAuthQuery a = QuickBooksOAuthRequest (QuickBooksResponse a) 
+
+data QuickBooksOAuthRequest a where
+  GetTempOAuthCredentials :: CallbackURL   -> QuickBooksOAuthQuery OAuthToken
+  GetAccessTokens         :: OAuthVerifier -> QuickBooksOAuthQuery OAuthToken
+  DisconnectQuickBooks    :: QuickBooksOAuthQuery ()
+
+data QuickBooksRequest a where
+  CreateInvoice           :: Invoice     -> QuickBooksQuery Invoice
+  ReadInvoice             :: InvoiceId   -> QuickBooksQuery Invoice
+  UpdateInvoice           :: Invoice     -> QuickBooksQuery Invoice
+  DeleteInvoice           :: InvoiceId   -> SyncToken -> QuickBooksQuery DeletedInvoice
+  SendInvoice             :: InvoiceId   -> E.EmailAddress -> QuickBooksQuery Invoice
+
+  QueryCustomer           :: Text -> QuickBooksQuery [Customer]
+  QueryItem               :: Text -> QuickBooksQuery [Item]
+
+newtype InvoiceId = InvoiceId {unInvoiceId :: Text}
+  deriving (Show, Eq, FromJSON, ToJSON)
+
+newtype LineId    = LineId {unLineId :: Text}
+  deriving (Show, Eq, FromJSON, ToJSON)
+
+newtype SyncToken = SyncToken { unSyncToken :: Text }
+  deriving (Show, Eq, FromJSON, ToJSON)
+
+-- | Details of a description line.
+
+data DescriptionLineDetail = DescriptionLineDetail
+  { descriptionLineDetailServiceDate :: !(Maybe Text)
+  , descriptionLineDetailTaxCodeRef  :: !(Maybe TaxCodeRef)
+  }
+  deriving (Show, Eq)
+
+-- | Details of a discount line.
+
+data DiscountLineDetail = DiscountLineDetail
+  { discountLineDetailDiscountRef        :: !(Maybe DiscountRef)
+  , discountLineDetailPercentBased       :: !(Maybe Bool)
+  , discountLineDetailDiscountPercent    :: !(Maybe Double)
+  , discountLineDetailDiscountAccountRef :: !(Maybe DiscountAccountRef)
+  }
+  deriving (Show, Eq)
+
+-- | Details of a sales item line.
+-- In order to create a sales item line detail, use 'salesItemLineDetail'.
+
+data SalesItemLineDetail = SalesItemLineDetail
+  { salesItemLineDetailItemRef         :: !(Maybe ItemRef)
+  , salesItemLineDetailClassRef        :: !(Maybe ClassRef)
+  , salesItemLineDetailUnitPrice       :: !(Maybe Double)
+  , salesItemLineDetailRatePercent     :: !(Maybe Double)
+  , salesItemLineDetailPriceLevelRef   :: !(Maybe PriceLevelRef)
+  , salesItemLineDetailMarkupInfo      :: !(Maybe Text)
+  , salesItemLineDetailQty             :: !(Maybe Double)
+  , salesItemLineDetailTaxCodeRef      :: !(Maybe TaxCodeRef)
+  , salesItemLineDetailServiceData     :: !(Maybe Text)
+  , salesItemLineDetailTaxInclusiveAmt :: !(Maybe Double)
+  }
+  deriving (Show, Eq)
+
+-- | Create a sales item line detail with a reference to an item.
+--
+-- Example:
+--
+-- >>> let aSalesItemLineDetail = salesItemLineDetail ((reference "1") {referenceName = Just "Services"})
+-- >>> salesItemLineDetailItemRef aSalesItemLineDetail
+-- Just (Reference {referenceName = Just "Services", referenceType = Nothing, referenceValue = "1"})
+
+salesItemLineDetail :: ItemRef -> SalesItemLineDetail
+salesItemLineDetail itemRef =
+  SalesItemLineDetail (Just itemRef)
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+                      Nothing
+
+-- | Details of a subtotal line.
+
+data SubTotalLineDetail = SubTotalLineDetail
+  { subtotalLineDetailItemRef :: !(Maybe ItemRef) }
+  deriving (Show, Eq)
+
+-- | An individual line item of a transaction.
+
+data Line = Line
+  { lineId                    :: !(Maybe LineId)
+  , lineLineNum               :: !(Maybe Double)
+  , lineDescription           :: !(Maybe Text)
+  , lineAmount                :: !(Maybe Double)
+  , lineLinkedTxn             :: !(Maybe [LinkedTxn])
+  , lineDetailType            :: !Text
+  , lineDescriptionLineDetail :: !(Maybe DescriptionLineDetail)
+  , lineDiscountLineDetail    :: !(Maybe DiscountLineDetail)
+  , lineSalesItemLineDetail   :: !(Maybe SalesItemLineDetail)
+  , lineSubTotalLineDetail    :: !(Maybe SubTotalLineDetail)
+  , lineCustomField           :: !(Maybe [CustomField])
+  }
+  deriving (Show, Eq)
+
+-- | Create a sales item line with amount and details.
+--
+-- Example:
+--
+-- >>> let aSalesItemLineDetail = salesItemLineDetail ((reference "1") {referenceName = Just "Services"})
+-- >>> let aSalesItemLine = salesItemLine 100.0 aSalesItemLineDetail
+
+salesItemLine :: Double
+              -> SalesItemLineDetail
+              -> Line
+salesItemLine amount detail =
+  Line Nothing
+       Nothing
+       Nothing
+       (Just amount)
+       Nothing
+       "SalesItemLineDetail"
+       Nothing
+       Nothing
+       (Just detail)
+       Nothing
+       Nothing
+-- | if you are using a salesItemLineDetail that has a Qty and a price it generates amount automatically
+emptySalesItemLine :: Line
+emptySalesItemLine =
+  Line Nothing
+       Nothing
+       Nothing
+       Nothing
+       Nothing
+       "SalesItemLineDetail"
+       Nothing
+       Nothing
+       Nothing
+       Nothing
+       Nothing
+
+
+newtype DeletedInvoiceId = DeletedInvoiceId { unDeletedInvoiceId :: Text }
+  deriving (Show, Eq, FromJSON, ToJSON)
+
+data DeletedInvoice = DeletedInvoice
+  { deletedInvoiceId     :: !DeletedInvoiceId
+  , deletedInvoicedomain :: !Text
+  , deletedInvoicestatus :: !Text
+  } deriving (Show, Eq)
+
+-- | A reference.
+-- In order to create a reference, use 'reference'.
+
+data Reference = Reference
+  { referenceName  :: !(Maybe Text)
+  , referenceType  :: !(Maybe Text)
+  , referenceValue :: !Text
+  }
+  deriving (Show, Eq)
+
+-- | Create a reference with a value.
+--
+-- Example:
+--
+-- >>> reference "21"
+-- Reference {referenceName = Nothing, referenceType = Nothing, referenceValue = "21"}
+
+reference :: Text -> Reference
+reference = Reference Nothing Nothing
+
+type ClassRef            = Reference
+type CurrencyRef         = Reference
+
+-- | A reference to a customer or a job.
+--
+-- Example:
+--
+-- >>> (reference "21") {referenceName = Just "John Doe"} :: CustomerRef
+-- Reference {referenceName = Just "John Doe", referenceType = Nothing, referenceValue = "21"}
+
+type CustomerRef         = Reference
+
+type DepartmentRef       = Reference
+type DepositToAccountRef = Reference
+type DiscountAccountRef  = Reference
+type DiscountRef         = Reference
+type ItemRef             = Reference
+type PriceLevelRef       = Reference
+type SalesTermRef        = Reference
+type ShipMethodRef       = Reference
+type TaxCodeRef          = Reference
+type TxnTaxCodeRef       = Reference
+
+data ModificationMetaData = ModificationMetaData
+  { modificationMetaDataCreateTime      :: !Text
+  , modificationMetaDataLastUpdatedTime :: !Text
+  }
+  deriving (Show, Eq)
+
+data TelephoneNumber = TelephoneNumber
+  { telephoneNumberFreeFormNumber :: !Text
+  }
+  deriving (Eq, Show)
+
+data WebSiteAddress = WebAddress
+  { webSiteAddressURI :: !Text
+  }
+  deriving (Eq, Show)
+
+data PhysicalAddress = PhysicalAddress
+  { physicalAddressId                     :: !Text
+  , physicalAddressLine1                  :: !Text
+  , physicalAddressLine2                  :: !(Maybe Text)
+  , physicalAddressLine3                  :: !(Maybe Text)
+  , physicalAddressLine4                  :: !(Maybe Text)
+  , physicalAddressLine5                  :: !(Maybe Text)
+  , physicalAddressCity                   :: !Text
+  , physicalAddressCountry                :: !(Maybe Text)
+  , physicalAddressCountrySubDivisionCode :: !Text
+  , physicalAddressPostalCode             :: !Text
+  , physicalAddressNote                   :: !(Maybe Text)
+  , physicalAddressLat                    :: !(Maybe Text)
+  , physicalAddressLong                   :: !(Maybe Text)
+  }
+  deriving (Show, Eq)
+
+type BillAddr = PhysicalAddress
+type ShipAddr = PhysicalAddress
+
+data EmailAddress = EmailAddress
+  { emailAddress :: !Text
+  }
+  deriving (Show, Eq)
+
+data TxnTaxDetail = TxnTaxDetail
+  { txnTaxDetailTxnTaxCodeRef :: !(Maybe TxnTaxCodeRef)
+  , txnTaxDetailTotalTax      :: !Double
+  , txnTaxDetailTaxLine       :: !(Maybe Line)
+  }
+  deriving (Show, Eq)
+
+data DeliveryInfo = DeliveryInfo
+  { deliveryInfoDeliveryType :: !(Maybe Text)
+  , deliveryInfoDeliveryTime :: !(Maybe Text)
+  }
+  deriving (Show, Eq)
+
+data LinkedTxn = LinkedTxn
+  { linkedTxnId     :: !(Maybe Text)
+  , linkedTxnType   :: !(Maybe Text)
+  , linkedTxnLineId :: !(Maybe Text)
+  }
+  deriving (Show, Eq)
+
+data CustomField = CustomField
+  { customFieldDefinitionId :: !Text
+  , customFieldName         :: !Text
+  , customFieldType         :: !CustomFieldType
+  , customFieldStringValue  :: !(Maybe Text)
+  , customFieldBooleanValue :: !(Maybe Bool)
+  , customFieldDateValue    :: !(Maybe Text)
+  , customFieldNumberValue  :: !(Maybe Double)
+  }
+  deriving (Show, Eq)
+
+data CustomFieldType
+  = BooleanType
+  | DateType
+  | NumberType
+  | StringType
+  deriving (Show, Eq)
+
+data GlobalTaxModel
+  = NotApplicable
+  | TaxExcluded
+  | TaxInclusive
+  deriving (Show, Eq)
+
+-- | An invoice transaction entity, that is, a sales form where the customer
+-- pays for a product or service later.
+--
+-- Business rules:
+--
+--   * An invoice must have at least one 'Line' that describes an item.
+--   * An invoice must have a 'CustomerRef'.
+--
+-- In order to create an invoice, use 'defaultInvoice'.
+
+data Invoice = Invoice
+  { invoiceId                    :: !(Maybe InvoiceId)
+  , invoiceSyncToken             :: !(Maybe SyncToken)
+  , invoiceMetaData              :: !(Maybe ModificationMetaData)
+  , invoiceCustomField           :: !(Maybe [CustomField])
+  , invoiceDocNumber             :: !(Maybe Text)
+  , invoiceTxnDate               :: !(Maybe Text)
+  , invoiceDepartmentRef         :: !(Maybe DepartmentRef)
+  , invoiceCurrencyRef           :: !(Maybe CurrencyRef) -- Non-US
+  , invoiceExchangeRate          :: !(Maybe Double) -- Non-US
+  , invoicePrivateNote           :: !(Maybe Text)
+  , invoiceLinkedTxn             :: !(Maybe [LinkedTxn])
+  , invoiceLine                  :: ![Line]
+  , invoiceTxnTaxDetail          :: !(Maybe TxnTaxDetail)
+  , invoiceCustomerRef           :: !CustomerRef
+  , invoiceCustomerMemo          :: !(Maybe Text)
+  , invoiceBillAddr              :: !(Maybe BillAddr)
+  , invoiceShipAddr              :: !(Maybe ShipAddr)
+  , invoiceClassRef              :: !(Maybe ClassRef)
+  , invoiceSalesTermRef          :: !(Maybe SalesTermRef)
+  , invoiceDueDate               :: !(Maybe Text)
+  , invoiceGlobalTaxCalculation  :: !(Maybe GlobalTaxModel) -- Non-US
+  , invoiceShipMethodRef         :: !(Maybe ShipMethodRef)
+  , invoiceShipDate              :: !(Maybe Text)
+  , invoiceTrackingNum           :: !(Maybe Text)
+  , invoiceTotalAmt              :: !(Maybe Double)
+  , invoiceHomeTotalAmt          :: !(Maybe Double) -- Non-US
+  , invoiceApplyTaxAfterDiscount :: !(Maybe Bool)
+  , invoicePrintStatus           :: !(Maybe Text)
+  , invoiceEmailStatus           :: !(Maybe Text)
+  , invoiceBillEmail             :: !(Maybe EmailAddress)
+  , invoiceDeliveryInfo          :: !(Maybe DeliveryInfo)
+  , invoiceBalance               :: !(Maybe Double)
+  , invoiceDepositToAccountRef   :: !(Maybe DepositToAccountRef)
+  , invoiceDeposit               :: !(Maybe Double)
+
+  , invoiceAllowIPNPayment       :: !(Maybe Bool)
+  , invoiceDomain                :: !(Maybe Text)
+  , invoiceSparse                :: !(Maybe Bool)
+  }
+  deriving (Show, Eq)
+
+-- | Create an 'Invoice' with the minimum elements.
+--
+-- Example:
+--
+-- >>> let customer21 = reference "21" :: CustomerRef
+-- >>> let aSalesItemLineDetail = salesItemLineDetail ((reference "1") {referenceName = Just "Services"})
+-- >>> let aSalesItemLine = salesItemLine 100.0 aSalesItemLineDetail
+--
+-- >>> let anInvoice = defaultInvoice [aSalesItemLine] customer21
+
+defaultInvoice :: [Line]      -- ^ The line items of a transaction
+               -> CustomerRef -- ^ Reference to a customer or a job
+               -> Invoice
+defaultInvoice [] _ = error "Bad invoice"
+defaultInvoice lines customerRef =
+  Invoice Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          lines
+          Nothing
+          customerRef
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+
+data InvoiceResponse = InvoiceResponse
+  { invoiceResponseInvoice :: Invoice }
+  deriving (Show, Eq)
+
+data CustomerResponse = CustomerResponse
+  { customerResponseCustomer :: !Customer
+  }
+  deriving (Eq, Show)
+
+data Customer = Customer
+  { customerId                      :: !(Maybe Text)
+  , customerSyncToken               :: !(Maybe SyncToken)
+  , customerMetaData                :: !(Maybe ModificationMetaData)
+  , customerTitle                   :: !(Maybe Text) -- def null
+  , customerGivenName               :: !(Maybe Text) -- max 25 def null
+  , customerMiddleName              :: !(Maybe Text) -- max 25, def null
+  , customerFamilyName              :: !(Maybe Text) -- max 25, def null
+  , customerSuffix                  :: !(Maybe Text) -- max 10, def null
+  , customerFullyQualifiedName      :: !(Maybe Text)
+  , customerCompanyName             :: !(Maybe Text) -- max 50, def null
+  , customerDisplayName             :: !Text -- unique
+  , customerPrintOnCheckName        :: !(Maybe Text) -- max 100
+  , customerActive                  :: !(Maybe Bool) -- def true
+  , customerPrimaryPhone            :: !(Maybe TelephoneNumber)
+  , customerAlternatePhone          :: !(Maybe TelephoneNumber)
+  , customerMobile                  :: !(Maybe TelephoneNumber)
+  , customerFax                     :: !(Maybe TelephoneNumber)
+  , customerPrimaryEmailAddress     :: !(Maybe EmailAddress)
+  , customerWebAddr                 :: !(Maybe WebSiteAddress)
+  , customerDefaultTaxCodeRef       :: !(Maybe TaxCodeRef)
+  , customerTaxable                 :: !(Maybe Bool)
+  , customerBillAddr                :: !(Maybe BillAddr)
+  , customerShipAddr                :: !(Maybe ShipAddr)
+  , customerNotes                   :: !(Maybe Text) -- max 2000
+  , customerJob                     :: !(Maybe Bool) -- def false or null
+  , customerBillWithParent          :: !(Maybe Bool) -- def false or null
+  , customerParentRef               :: !(Maybe CustomerRef)
+  , customerLevel                   :: !(Maybe Int) -- def 0, up to 5
+  , customerSalesTermRef            :: !(Maybe SalesTermRef)
+  , customerPaymentMethodRef        :: !(Maybe Reference)
+  , customerBalance                 :: !(Maybe Double)
+  , customerOpenBalanceDate         :: !(Maybe Text)
+  , customerBalanceWithJobs         :: !(Maybe Double)
+  , customerCurrencyRef             :: !(Maybe CurrencyRef)
+  , customerPreferredDeliveryMethod :: !(Maybe Text)
+  , customerResaleNum               :: !(Maybe Text) -- max 15
+  }
+  deriving (Eq, Show)
+
+data ItemResponse = ItemResponse
+  { itemResponseItem :: !Item
+  }
+  deriving (Eq, Show)
+
+data Item = Item
+  { itemId                   :: !(Maybe Text)
+  , itemSyncToken            :: !(Maybe SyncToken)
+  , itemMetaData             :: !(Maybe ModificationMetaData)
+  , itemName                 :: !Text -- max 100
+  , itemDescription          :: !(Maybe Text) -- max 4000
+  , itemActive               :: !(Maybe Bool) -- def true
+  , itemSubItem              :: !(Maybe Bool) -- def false or null
+  , itemParentRef            :: !(Maybe ItemRef) -- def null
+  , itemLevel                :: !(Maybe Int) -- def 0 up to 5
+  , itemFullyQualifiedName   :: !(Maybe String) -- def null
+  , itemTaxable              :: !(Maybe Bool) -- US only
+  , itemSalesTaxInclusive    :: !(Maybe Bool) -- def false
+  , itemUnitPrice            :: !(Maybe Double) -- max 99999999999, def 0
+  , itemType                 :: !(Maybe Text) -- def Inventory (Inventory/Service)
+  , itemIncomeAccountRef     :: !(Maybe Reference) -- required
+  , itemPurchaseDesc         :: !(Maybe String) -- max 1000
+  , itemPurchaseTaxInclusive :: !(Maybe Bool) -- def false
+  , itemPurchaseCost         :: !(Maybe Double) -- max 99999999999
+  , itemExpenseAccountRef    :: !(Maybe Reference) -- required
+  , itemAssetAccountRef      :: !(Maybe Reference) -- req for inventory items
+  , itemTrackQtyOnHand       :: !(Maybe Bool) -- def false
+  , itemQtyOnHand            :: !(Maybe Double) -- req for inventory items
+  , itemSalesTaxCodeRef      :: !(Maybe Reference)
+  , itemPurchaseTaxCodeRef   :: !(Maybe Reference)
+  , itemInvStartDate         :: !(Maybe Text) -- required for inventory items
+  }
+  deriving (Eq, Show)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 8
+               , omitNothingFields  = True
+               }
+             ''Customer)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 16
+               }
+             ''CustomerResponse)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 4
+               , omitNothingFields  = True
+               }
+             ''Item)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 12
+               }
+             ''ItemResponse)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 15
+               }
+             ''TelephoneNumber)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 14
+               }
+             ''WebSiteAddress)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 11
+               , omitNothingFields  = True }
+             ''CustomField)
+
+$(deriveJSON defaultOptions
+             ''CustomFieldType)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 12 }
+             ''DeliveryInfo)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 21
+               , omitNothingFields  = True }
+             ''DescriptionLineDetail)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 18
+               , omitNothingFields  = True }
+             ''DiscountLineDetail)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 5 }
+             ''EmailAddress)
+
+$(deriveJSON defaultOptions
+             ''GlobalTaxModel)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 7
+               , omitNothingFields  = True }
+             ''Invoice)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 15 }
+             ''InvoiceResponse)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 4 }
+             ''Line)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 6
+               , omitNothingFields  = True }
+             ''LinkedTxn)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 20 }
+             ''ModificationMetaData)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 15 }
+             ''PhysicalAddress)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = map toLower . drop 9
+               , omitNothingFields  = True }
+             ''Reference)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 19
+               , omitNothingFields  = True }
+             ''SalesItemLineDetail)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 18
+               , omitNothingFields  = True }
+             ''SubTotalLineDetail)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop 12 }
+             ''TxnTaxDetail)
+
+$(deriveJSON defaultOptions
+               { fieldLabelModifier = drop (length ("deletedInvoice" :: String)) }
+             ''DeletedInvoice)
diff --git a/test/Data.hs b/test/Data.hs
new file mode 100644
--- /dev/null
+++ b/test/Data.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Data where
+
+import           Control.Monad         (ap, liftM)
+import           Data.ByteString.Char8 (pack)
+import           Data.Maybe            (fromJust)
+import           Data.String
+import           QuickBooks.Types
+import           System.Environment    (getEnvironment)
+import qualified Text.Email.Validate   as E (EmailAddress, emailAddress)
+
+lookupTestOAuthTokenFromEnv :: IO (Maybe OAuthToken)
+lookupTestOAuthTokenFromEnv = do
+  env <- getEnvironment
+  return $ OAuthToken `liftM` (pack `fmap` (lookup "INTUIT_TOKEN" env))
+                      `ap`    (pack `fmap` (lookup "INTUIT_SECRET" env))
+
+trashEmailAccount :: (IsString a) => a
+trashEmailAccount = "xvh221@sharklasers.com"
+
+testEmail :: E.EmailAddress
+testEmail = fromJust $ E.emailAddress trashEmailAccount
+
+testLine :: Line
+testLine = Line
+  { lineId                    = Nothing
+  , lineLineNum               = Nothing
+  , lineDescription           = Nothing
+  , lineAmount                = Just 100.0
+  , lineLinkedTxn             = Nothing
+  , lineDetailType            = "SalesItemLineDetail"
+  , lineDescriptionLineDetail = Nothing
+  , lineDiscountLineDetail    = Nothing
+  , lineSalesItemLineDetail   = Just testSalesItemLineDetail
+  , lineSubTotalLineDetail    = Nothing
+  , lineCustomField           = Nothing
+  }
+
+testSalesItemLineDetail :: SalesItemLineDetail
+testSalesItemLineDetail =
+  SalesItemLineDetail
+   { salesItemLineDetailItemRef         = Just testItemRef
+   , salesItemLineDetailClassRef        = Nothing
+   , salesItemLineDetailUnitPrice       = Nothing
+   , salesItemLineDetailRatePercent     = Nothing
+   , salesItemLineDetailPriceLevelRef   = Nothing
+   , salesItemLineDetailMarkupInfo      = Nothing
+   , salesItemLineDetailQty             = Nothing
+   , salesItemLineDetailTaxCodeRef      = Nothing
+   , salesItemLineDetailServiceData     = Nothing
+   , salesItemLineDetailTaxInclusiveAmt = Nothing
+  }
+
+
+testItemRef :: Reference
+testItemRef = Reference
+  { referenceValue = "1"
+  , referenceName  = Nothing
+  , referenceType  = Nothing
+  }
+
+
+testCustomerRef :: CustomerRef
+testCustomerRef  = Reference
+  { referenceValue = "21"
+  , referenceName  = Nothing
+  , referenceType  = Nothing
+  }
+
+testInvoice :: Invoice
+testInvoice =
+  Invoice Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          [testLine]
+          Nothing
+          testCustomerRef
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          (Just $ EmailAddress trashEmailAccount)
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+          Nothing
+
diff --git a/test/doctests.hs b/test/doctests.hs
new file mode 100644
--- /dev/null
+++ b/test/doctests.hs
@@ -0,0 +1,4 @@
+import Test.DocTest
+
+main :: IO ()
+main = doctest ["-isrc", "test/Data.hs", "src/QuickBooks.hs"]
