diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for voicebase
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Daisee (c) 2018
+
+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 Daisee nor the names of other
+      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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,42 @@
+# Haskell bindings for voice base api
+
+http://voicebase.readthedocs.io/en/v2-beta/
+
+## Usage
+
+```haskell
+    import VoicebaseClient
+    import Json.TranscriptTypes
+
+    main = do
+      result <- transcribe "your bearer token" "./audio.mp3"
+      -- get validated response or error
+      case result of
+        Left err -> print $ err
+        Right val -> putStrLn val
+
+      -- get a parsed structure
+      parsed <- transcribeParse "your bearer token" "./audio.mp3"
+      case parsed of 
+        Left err -> print $ err
+        Right val -> print $ latestTranscriptsWordsTranscripts . transcriptsLatestTranscripts . mediaTranscripts . topLevelMedia val
+
+```
+
+There is also a main file which shows usage
+
+
+## features
+
++ Post audio file to voicebase
++ Poll for progress
++ Get resulting transcript or valid json or an error.
+
+## Trivia
+### wreq client instead of servant ###
+
+The reason httplib is used rather than servant is because of multipart posting.
+It was asserted that servant is incapable of doing that.
+This is a requirement for the voicebase api however.
+Http client does support this however we need to  write more code as a result of this.
+
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/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Network.Mime(defaultMimeLookup)
+import VoicebaseClient
+import Options
+import qualified Data.ByteString.Lazy as LBS
+
+data MainOptions = MainOptions
+    { 
+      token :: Maybe BearerToken,
+      targetFile :: Maybe FilePath
+    }
+
+instance Options MainOptions where
+    defineOptions = pure MainOptions
+        <*> simpleOption "bearer-token" Nothing
+            "The bearer token obtained from voicebase"
+        <*> simpleOption "file" Nothing
+            "The file to be posted"
+main :: IO ()
+main = runCommand $ \opts args -> do
+  case token opts of
+    Just bearToken -> case targetFile opts of 
+      Just file -> withBearAndFile bearToken file
+      Nothing -> putStrLn "file required"
+    Nothing -> putStrLn "bearer token required"
+
+withBearAndFile :: BearerToken -> FilePath -> IO() 
+withBearAndFile bearToken file = do 
+  -- result <- transcribeFile bearToken file 
+  bytes <- LBS.readFile file
+  result <- transcribeBytes bearToken (LazyByteFile bytes (defaultMimeLookup "f.mp3"))
+  putStrLn . show $ result
diff --git a/src/Json/ProgressTypes.hs b/src/Json/ProgressTypes.hs
new file mode 100644
--- /dev/null
+++ b/src/Json/ProgressTypes.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE DeriveGeneric       #-}
+
+-- | Auto generated module by json-autotype
+module Json.ProgressTypes where
+
+import           System.Exit        (exitFailure, exitSuccess)
+import           System.IO          (stderr, hPutStrLn)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           Data.ByteString.Lazy
+import           System.Environment (getArgs)
+import           Control.Monad      (forM_, mzero, join)
+import           Control.Applicative
+import           Data.Aeson.AutoType.Alternative
+import           Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),
+                            pairs,
+                            (.:), (.:?), (.=), object)
+import           Data.Monoid
+import           Data.Text (Text)
+import qualified GHC.Generics
+
+-- | Workaround for https://github.com/bos/aeson/issues/287.
+o .:?? val = fmap join (o .:? val)
+
+
+data Tasks = Tasks { 
+
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Tasks where
+  parseJSON (Object v) = return  Tasks
+  parseJSON _          = mzero
+
+
+instance ToJSON Tasks where
+  toJSON     (Tasks {}) = object []
+
+
+
+data Progress = Progress { 
+    progressStatus :: Text,
+    progressJobId :: Text,
+    progressTasks :: Tasks
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Progress where
+  parseJSON (Object v) = Progress <$> v .:   "status" <*> v .:   "jobId" <*> v .:   "tasks"
+  parseJSON _          = mzero
+
+
+instance ToJSON Progress where
+  toJSON     (Progress {..}) = object ["status" .= progressStatus, "jobId" .= progressJobId, "tasks" .= progressTasks]
+  toEncoding (Progress {..}) = pairs  ("status" .= progressStatus<>"jobId" .= progressJobId<>"tasks" .= progressTasks)
+
+
+data TopLevel = TopLevel { 
+    topLevelStatus :: Text,
+    topLevelProgress :: Progress,
+    topLevelMediaId :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON TopLevel where
+  parseJSON (Object v) = TopLevel <$> v .:   "status" <*> v .:   "progress" <*> v .:   "mediaId"
+  parseJSON _          = mzero
+
+
+instance ToJSON TopLevel where
+  toJSON     (TopLevel {..}) = object ["status" .= topLevelStatus, "progress" .= topLevelProgress, "mediaId" .= topLevelMediaId]
+  toEncoding (TopLevel {..}) = pairs  ("status" .= topLevelStatus<>"progress" .= topLevelProgress<>"mediaId" .= topLevelMediaId)
+
+
+parse :: ByteString -> Either String TopLevel
+parse = eitherDecode
diff --git a/src/Json/SubmitMediaTypes.hs b/src/Json/SubmitMediaTypes.hs
new file mode 100644
--- /dev/null
+++ b/src/Json/SubmitMediaTypes.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE DeriveGeneric       #-}
+
+-- | Auto generated module by json-autotype
+module Json.SubmitMediaTypes where
+
+import           System.Exit        (exitFailure, exitSuccess)
+import           System.IO          (stderr, hPutStrLn)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           Data.ByteString.Lazy
+import           System.Environment (getArgs)
+import           Control.Monad      (forM_, mzero, join)
+import           Control.Applicative
+import           Data.Aeson.AutoType.Alternative
+import           Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),
+                            pairs,
+                            (.:), (.:?), (.=), object)
+import           Data.Monoid
+import           Data.Text (Text)
+import qualified GHC.Generics
+
+-- | Workaround for https://github.com/bos/aeson/issues/287.
+o .:?? val = fmap join (o .:? val)
+
+
+data Self = Self { 
+    selfHref :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Self where
+  parseJSON (Object v) = Self <$> v .:   "href"
+  parseJSON _          = mzero
+
+
+instance ToJSON Self where
+  toJSON     (Self {..}) = object ["href" .= selfHref]
+  toEncoding (Self {..}) = pairs  ("href" .= selfHref)
+
+
+data Links = Links { 
+    linksSelf :: Self
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Links where
+  parseJSON (Object v) = Links <$> v .:   "self"
+  parseJSON _          = mzero
+
+
+instance ToJSON Links where
+  toJSON     (Links {..}) = object ["self" .= linksSelf]
+  toEncoding (Links {..}) = pairs  ("self" .= linksSelf)
+
+
+data Metadata = Metadata { 
+
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Metadata where
+  parseJSON (Object v) = return  Metadata
+  parseJSON _          = mzero
+
+
+instance ToJSON Metadata where
+  toJSON     (Metadata {}) = object []
+
+
+
+data TopLevel = TopLevel { 
+    topLevelStatus :: Text,
+    topLevelMediaId :: Text,
+    topLevelLinks :: Links,
+    topLevelMetadata :: Metadata
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON TopLevel where
+  parseJSON (Object v) = TopLevel <$> v .:   "status" <*> v .:   "mediaId" <*> v .:   "_links" <*> v .:   "metadata"
+  parseJSON _          = mzero
+
+
+instance ToJSON TopLevel where
+  toJSON     (TopLevel {..}) = object ["status" .= topLevelStatus, "mediaId" .= topLevelMediaId, "_links" .= topLevelLinks, "metadata" .= topLevelMetadata]
+  toEncoding (TopLevel {..}) = pairs  ("status" .= topLevelStatus<>"mediaId" .= topLevelMediaId<>"_links" .= topLevelLinks<>"metadata" .= topLevelMetadata)
+
+
+
+parse :: ByteString -> Either String TopLevel
+parse = eitherDecode
diff --git a/src/Json/TranscriptTypes.hs b/src/Json/TranscriptTypes.hs
new file mode 100644
--- /dev/null
+++ b/src/Json/TranscriptTypes.hs
@@ -0,0 +1,234 @@
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE DeriveGeneric       #-}
+
+-- | Auto generated module by json-autotype
+module Json.TranscriptTypes where
+
+import           System.Exit        (exitFailure, exitSuccess)
+import           System.IO          (stderr, hPutStrLn)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           Data.ByteString.Lazy
+import           System.Environment (getArgs)
+import           Control.Monad      (forM_, mzero, join)
+import           Control.Applicative
+import           Data.Aeson.AutoType.Alternative
+import           Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),
+                            pairs,
+                            (.:), (.:?), (.=), object)
+import           Data.Monoid
+import           Data.Text (Text)
+import qualified GHC.Generics
+
+-- | Workaround for https://github.com/bos/aeson/issues/287.
+o .:?? val = fmap join (o .:? val)
+
+data LatestTopics = LatestTopics { 
+    latestTopicsTopics :: [(Maybe Value)],
+    latestTopicsRevision :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON LatestTopics where
+  parseJSON (Object v) = LatestTopics <$> v .:   "topics" <*> v .:   "revision"
+  parseJSON _          = mzero
+
+
+data Topics = Topics { 
+    topicsLatestTopics :: LatestTopics
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Topics where
+  parseJSON (Object v) = Topics <$> v .:   "latest"
+  parseJSON _          = mzero
+
+
+data Tasks = Tasks { 
+} deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Tasks where
+  parseJSON (Object v) = return  Tasks
+  parseJSON _          = mzero
+
+data Progress = Progress { 
+    progressStatus :: Text,
+    progressJobId :: Text,
+    progressTasks :: Tasks
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Progress where
+  parseJSON (Object v) = Progress <$> v .:   "status" <*> v .:   "jobId" <*> v .:   "tasks"
+  parseJSON _          = mzero
+
+
+data Job = Job { 
+    jobProgress :: Progress
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Job where
+  parseJSON (Object v) = Job <$> v .:   "progress"
+  parseJSON _          = mzero
+
+
+data T = T { 
+    tUnknown :: [Text]
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON T where
+  parseJSON (Object v) = T <$> v .:   "unknown"
+  parseJSON _          = mzero
+
+
+data WordsKeyElt = WordsKeyElt { 
+    wordsKeyEltRelevance :: Text,
+    wordsKeyEltT :: T,
+    wordsKeyEltName :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON WordsKeyElt where
+  parseJSON (Object v) = WordsKeyElt <$> v .:   "relevance" <*> v .:   "t" <*> v .:   "name"
+  parseJSON _          = mzero
+
+
+data LatestKeywords = LatestKeywords { 
+    latestKeywordsGroups :: [(Maybe Value)],
+    latestKeywordsWordsKey :: [WordsKeyElt],
+    latestKeywordsRevision :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON LatestKeywords where
+  parseJSON (Object v) = LatestKeywords <$> v .:   "groups" <*> v .:   "words" <*> v .:   "revision"
+  parseJSON _          = mzero
+
+
+data Keywords = Keywords { 
+    keywordsLatestKeywords :: LatestKeywords
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Keywords where
+  parseJSON (Object v) = Keywords <$> v .:   "latest"
+  parseJSON _          = mzero
+
+
+data Length = Length { 
+    lengthDescriptive :: Text,
+    lengthMilliseconds :: Double
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Length where
+  parseJSON (Object v) = Length <$> v .:   "descriptive" <*> v .:   "milliseconds"
+  parseJSON _          = mzero
+
+
+data Metadata = Metadata { 
+    metadataLength :: Length,
+    metadataContentType :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Metadata where
+  parseJSON (Object v) = Metadata <$> v .:   "length" <*> v .:   "contentType"
+  parseJSON _          = mzero
+
+
+data WordsTranscriptsElt = WordsTranscriptsElt { 
+    wordsTranscriptsEltW :: Text,
+    wordsTranscriptsEltM :: (Maybe (Text:|:[(Maybe Value)])),
+    wordsTranscriptsEltP :: Double,
+    wordsTranscriptsEltE :: Double,
+    wordsTranscriptsEltC :: Double,
+    wordsTranscriptsEltS :: Double
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON WordsTranscriptsElt where
+  parseJSON (Object v) = WordsTranscriptsElt <$> v .:   "w" <*> v .:?? "m" <*> v .:   "p" <*> v .:   "e" <*> v .:   "c" <*> v .:   "s"
+  parseJSON _          = mzero
+
+
+data LatestTranscripts = LatestTranscripts { 
+    latestTranscriptsEngine :: Text,
+    latestTranscriptsConfidence :: Double,
+    latestTranscriptsName :: Text,
+    latestTranscriptsWordsTranscripts :: [WordsTranscriptsElt],
+    latestTranscriptsRevision :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON LatestTranscripts where
+  parseJSON (Object v) = LatestTranscripts <$> v .:   "engine" <*> v .:   "confidence" <*> v .:   "name" <*> v .:   "words" <*> v .:   "revision"
+  parseJSON _          = mzero
+
+
+data Transcripts = Transcripts { 
+    transcriptsLatestTranscripts :: LatestTranscripts
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Transcripts where
+  parseJSON (Object v) = Transcripts <$> v .:   "latest"
+  parseJSON _          = mzero
+
+
+data Media = Media { 
+    mediaStatus :: Text,
+    mediaTopics :: Topics,
+    mediaMediaId :: Text,
+    mediaDateCreated :: Text,
+    mediaJob :: Job,
+    mediaKeywords :: Keywords,
+    mediaMetadata :: Metadata,
+    mediaTranscripts :: Transcripts,
+    mediaDateFinished :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Media where
+  parseJSON (Object v) = Media <$> v .:   "status" <*> v .:   "topics" <*> v .:   "mediaId" <*> v .:   "dateCreated" <*> v .:   "job" <*> v .:   "keywords" <*> v .:   "metadata" <*> v .:   "transcripts" <*> v .:   "dateFinished"
+  parseJSON _          = mzero
+
+
+data Self = Self { 
+    selfHref :: Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Self where
+  parseJSON (Object v) = Self <$> v .:   "href"
+  parseJSON _          = mzero
+
+
+data Links = Links { 
+    linksSelf :: Self
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Links where
+  parseJSON (Object v) = Links <$> v .:   "self"
+  parseJSON _          = mzero
+
+
+data Transcript = Transcript { 
+    topLevelMedia :: Media,
+    topLevelLinks :: Links
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Transcript where
+  parseJSON (Object v) = Transcript <$> v .:   "media" <*> v .:   "_links"
+  parseJSON _          = mzero
+
+parse :: ByteString -> Either String Transcript
+parse = eitherDecode
diff --git a/src/VoicebaseClient.hs b/src/VoicebaseClient.hs
new file mode 100644
--- /dev/null
+++ b/src/VoicebaseClient.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | Main module for doing the requests to the voicebase api: http://voicebase.readthedocs.io/en/v2-beta/
+module VoicebaseClient
+    (
+      transcribe, 
+      transcribeFile, 
+      transcribeBytes, 
+      transcribeParse, 
+      BearerToken, 
+      Error(..),
+      LazyByteFile(..)
+  ) where
+
+import Control.Lens
+import Control.Monad
+import Control.Applicative
+
+import Data.Maybe
+import Data.Monoid
+import Data.Text.Encoding as TE
+import Data.Bifunctor
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as C8LBS
+import qualified Data.ByteString.Lazy as LBS
+import Data.Text (Text, unpack)
+
+import Data.Aeson(Value, eitherDecode, FromJSON(parseJSON))
+import Data.Aeson.Types(parseEither)
+import Network.Wreq
+import Network.Mime(MimeType)
+import qualified Network.Wreq.Session as Sess
+import Network.HTTP.Client.OpenSSL
+import Network.HTTP.Client (defaultManagerSettings, managerResponseTimeout,  responseTimeoutMicro)
+import OpenSSL.Session (context)
+
+import qualified Json.SubmitMediaTypes as Submit
+import qualified Json.ProgressTypes as Progress
+import qualified Json.TranscriptTypes as Transcript
+
+import System.IO(fixIO)
+
+-- | get your bearer token at http://voicebase.readthedocs.io/en/v2-beta/how-to-guides/hello-world.html#token
+type BearerToken = String
+
+data Error = UnkownResponse | ParseError String
+  deriving(Show)
+
+url :: String
+url = "https://apis.voicebase.com/v2-beta/media"
+
+timeout = responseTimeoutMicro 10000
+
+makeOpts :: BearerToken -> Options
+makeOpts token = withAuthorization token defaults
+
+withAuthorization :: BearerToken -> Options -> Options
+withAuthorization token options = options & manager .~ Left (opensslManagerSettings context) 
+  & manager .~ Left (defaultManagerSettings { managerResponseTimeout = timeout} )
+  & header "Authorization" .~ [(BS.pack ("Bearer " ++ token))]
+
+-- | transcribes the audio file and puts it into a Transcript
+transcribeParse  :: BearerToken -> FilePath -> IO(Either Error Transcript.Transcript)
+transcribeParse token filepath = fmap (join . second (first ParseError . parseEither parseJSON)) (transcribeFile token filepath)
+
+inputName = "media"
+-- | Given a bearer token, and a filepath to an audio file, this function will 
+-- | eventually return a transcript or times out after 10 seconds
+-- | Throws HttpExceptionRequest, IOException (file not found)
+transcribeFile :: BearerToken -> FilePath -> IO(Either Error Value)
+transcribeFile bearerToken filePath = transcribe bearerToken defaults (
+    [partFileSource inputName filePath]
+  )
+
+-- | In case of a bytestring, we also need to mimetype to decode the send string
+data LazyByteFile = LazyByteFile {
+  content :: LBS.ByteString,
+  mimetype :: MimeType -- to get this Jappie used defaultMimeLookup "f.mp3"
+  }
+-- | Transcribe a bytestring
+-- | Throws HttpExceptionRequest
+transcribeBytes :: BearerToken -> LazyByteFile -> IO(Either Error Value)
+transcribeBytes bearerToken (LazyByteFile content mimetype) = 
+  transcribe bearerToken defaults (
+    [partLBS inputName content & partContentType .~ Just mimetype ]
+  )
+
+-- | Generic transcribe, agnostic of options, will add ssl, 
+-- | the bearer token and a timeout to the options.
+-- | Throws HttpExceptionRequest
+transcribe :: BearerToken -> Options -> [Part] -> IO(Either Error Value)
+transcribe token options parts = do
+  session <- Sess.newSession
+  response <- Sess.postWith (withAuthorization token options) session url parts
+  case Submit.parse (response ^. responseBody) of
+    Left error -> return $ Left $ ParseError error
+    Right parsed -> do 
+      -- wait for the service to complete
+      waitResult <- pollStatus token session (Submit.topLevelMediaId parsed)
+      case waitResult of
+        Right _ -> first ParseError . eitherDecode <$> (requestTranscript token session (Submit.topLevelMediaId parsed)) 
+        Left error -> return $ Left $ error
+
+
+pollStatus :: BearerToken -> Sess.Session -> Text -> IO(Either Error ())
+pollStatus token session mediaId = pollStatus' token session mediaId "pending"
+
+pollStatus' :: BearerToken -> Sess.Session -> Text -> Text -> IO (Either Error ())
+pollStatus' token session mediaId "pending" = do
+  response <- Sess.getWith (makeOpts token) session (url <> "/" <> (unpack mediaId) <> "/progress")
+  case Progress.parse (response ^. responseBody) of 
+    Left error -> return $ Left $ ParseError error
+    Right parsed -> pollStatus' token session mediaId ((Progress.progressStatus . Progress.topLevelProgress) $ parsed)
+pollStatus' token session mediaId "started" = pollStatus' token session mediaId "pending"
+pollStatus' token session mediaId "completed" = return $ Right ()
+pollStatus' _ _ _ _ = return $ Left UnkownResponse
+
+requestTranscript :: BearerToken -> Sess.Session -> Text -> IO (C8LBS.ByteString)
+requestTranscript token session mediaId = do
+  response <- Sess.getWith (makeOpts token) session (url <> "/" <> (unpack mediaId))
+  return $ response ^. responseBody
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
diff --git a/voicebase.cabal b/voicebase.cabal
new file mode 100644
--- /dev/null
+++ b/voicebase.cabal
@@ -0,0 +1,74 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 1d2c9271b864ce3c92a5289357729a078bb7fa8814ebedf3ccf470a746a16270
+
+name:           voicebase
+version:        0.1.1.0
+synopsis:       Upload audio files to voicebase to get a transcription
+description:    voicebase bindings for <http://voicebase.readthedocs.io/en/v2-beta/>
+category:       API
+author:         Jappie Klooster
+maintainer:     jappie.klooster@daisee.com
+copyright:      Daisee Pty Ltd
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+extra-source-files:
+    ChangeLog.md
+    README.md
+
+library
+  hs-source-dirs:
+      src
+  build-depends:
+      HsOpenSSL
+    , aeson
+    , base >=4.7 && <5
+    , bytestring
+    , http-client
+    , http-client-openssl
+    , json-autotype
+    , lens
+    , mime-types
+    , text
+    , wreq
+  exposed-modules:
+      Json.ProgressTypes
+      Json.SubmitMediaTypes
+      Json.TranscriptTypes
+      VoicebaseClient
+  other-modules:
+      Paths_voicebase
+  default-language: Haskell2010
+
+executable voicebase
+  main-is: Main.hs
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , bytestring
+    , mime-types
+    , options
+    , voicebase
+  other-modules:
+      Paths_voicebase
+  default-language: Haskell2010
+
+test-suite voicebase-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , voicebase
+  other-modules:
+      Paths_voicebase
+  default-language: Haskell2010
