diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for speechmatics
+
+## 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 Jappie Klooster 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,9 @@
+# speechmatics
+
+a client for the [speechmatics](https://app.speechmatics.com/api-details) api .
+
+with input an audio file and blocking untill we get a json transcription.
+
+
+Developed by [Daisee](https://www.daisee.com/), pull requests are accepted:
+https://bitbucket.org/daisee/speechmatics-api-client
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,44 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Network.Mime(defaultMimeLookup)
+import Options
+import qualified Data.ByteString.Lazy as LBS
+import Data.Digest.Pure.SHA
+import Speechmatics.Client 
+import Network.Mime(MimeType)
+
+data MainOptions = MainOptions
+    { 
+      user_id :: Maybe UserID,
+      token :: Maybe AuthToken,
+      targetFile :: Maybe FilePath
+    }
+
+
+instance Options MainOptions where
+    defineOptions = pure MainOptions
+        <*> simpleOption "userId" Nothing
+            "The user id obtained from speechmatics"
+        <*> simpleOption "token" Nothing
+            "The bearer token obtained from speechmatics"
+        <*> simpleOption "file" Nothing
+            "The file to be posted"
+
+main :: IO ()
+main = runCommand $ \opts args -> do
+  case withBearAndFile <$> user_id opts <*> token opts <*> targetFile opts of
+    Nothing -> print "wrong options, see --help"
+    Just compute -> compute
+
+withBearAndFile :: UserID -> AuthToken -> FilePath -> IO() 
+withBearAndFile userId bearToken file = do 
+  -- result <- transcribeFile bearToken file 
+  bytes <- LBS.readFile file
+  let mime = (defaultMimeLookup "d.mp3")
+  print mime
+  print . sha256 $ bytes
+  result <- transcribeBytes userId bearToken "en-US" (LazyByteFile bytes file mime)
+  putStrLn . show $ result
+
diff --git a/speechmatics.cabal b/speechmatics.cabal
new file mode 100644
--- /dev/null
+++ b/speechmatics.cabal
@@ -0,0 +1,81 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 4bc71e263d2002b605c0009ece685cd69f43493ceabe7ba3da924eaaf3e2cb69
+
+name:           speechmatics
+version:        0.1.0.0
+synopsis:       Upload audio files to speechmatics to get a transcription
+description:    Please see the README on bitbucket <https://bitbucket.org/daisee/speechmatics-api-client/src/master/>
+category:       API
+homepage:       https://bitbucket.org/daisee/speechmatics-api-client/
+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:
+      Speechmatics.Client
+      Speechmatics.JSON.PeekJob
+      Speechmatics.JSON.PostJob
+  other-modules:
+      Paths_speechmatics
+  default-language: Haskell2010
+
+executable speechmatics
+  main-is: Main.hs
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      SHA
+    , base >=4.7 && <5
+    , bytestring
+    , mime-types
+    , options
+    , speechmatics
+  other-modules:
+      Paths_speechmatics
+  default-language: Haskell2010
+
+test-suite speechmatics-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
+    , bytestring
+    , hspec
+    , neat-interpolation
+    , speechmatics
+    , text
+  other-modules:
+      PeekJobSpec
+      PostJobSpec
+      Paths_speechmatics
+  default-language: Haskell2010
diff --git a/src/Speechmatics/Client.hs b/src/Speechmatics/Client.hs
new file mode 100644
--- /dev/null
+++ b/src/Speechmatics/Client.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Speechmatics.Client(
+  transcribeBytes,
+  transcribe,
+  AuthToken,
+  UserID,
+  ModelName,
+  LazyByteFile(..)
+) where
+
+import           Control.Concurrent         (threadDelay)
+import OpenSSL.Session (context)
+import Control.Lens
+import Control.Monad
+import Control.Applicative
+
+import Data.Aeson(Value, eitherDecode, FromJSON(parseJSON))
+import Network.HTTP.Client (defaultManagerSettings, managerResponseTimeout,  responseTimeoutMicro)
+
+import Network.Wreq
+import Network.Mime(MimeType)
+import qualified Network.Wreq.Session as Sess
+import Network.HTTP.Client.OpenSSL
+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 qualified Network.Wreq.Session as Sess
+import Data.Text
+import Data.Monoid
+import qualified Speechmatics.JSON.PostJob as Post
+import qualified Speechmatics.JSON.PeekJob as Peek
+
+-- | Authorization token obtained from speechmatics
+type AuthToken = String
+-- | UserID obtained from speechmatics
+type UserID = Integer
+-- | Which langauge model to use
+type ModelName = String
+type JobID = Integer
+
+data LazyByteFile = LazyByteFile {
+  content :: LBS.ByteString,
+  filename :: String,
+  mimetype :: MimeType -- to get this Jappie used defaultMimeLookup "f.mp3"
+  }
+data Error = UnkownResponse | ParseError String
+  deriving(Show)
+
+makeOpts :: Options
+makeOpts = withAuthorization defaults
+
+timeout = responseTimeoutMicro 10000
+
+url :: String
+url = "https://api.speechmatics.com/v1.0/user/"
+
+inputName :: Text
+inputName = pack "data_file"
+
+modelName :: Text
+modelName = pack "model"
+
+-- | Transcribe a file that is already loaded in memory
+--   Does not catch exeptions
+transcribeBytes :: UserID -> AuthToken -> ModelName -> LazyByteFile -> IO(Either Error Value)
+transcribeBytes userID bearerToken model (LazyByteFile content filename mimetype) = do
+  transcribe userID bearerToken defaults parts
+  where
+    parts = [
+        partLBS inputName content 
+          & partContentType .~ Just mimetype 
+          & partFileName .~ Just filename,
+        partString modelName model
+      ]
+
+withAuthorization :: Options -> Options
+withAuthorization options = options 
+  & manager .~ Left (opensslManagerSettings context) 
+  & manager .~ Left (defaultManagerSettings { managerResponseTimeout = timeout} )
+
+tokenUri :: AuthToken -> String
+tokenUri auth = "?auth_token=" <> auth
+
+slash = mappend . (flip mappend "/") -- play around to much with ghci
+jobsUri :: UserID -> String -> String
+jobsUri userID x = url <> (show userID) `slash` "jobs" `slash` x
+
+-- | More general transcription interface, allows custom parts to be inserted 
+--   with whatever the user wants.
+--   Does not catch exeptions
+transcribe :: UserID -> AuthToken -> Options -> [Part] -> IO(Either Error Value)
+transcribe userID token options parts = do
+  session <- Sess.newSession
+  response <- Sess.postWith auth session postUri parts
+  case Post.parse (response ^. responseBody) of
+    Left message -> return $ Left $ ParseError message
+    Right parsed -> do
+      let jobID = Post.postId parsed
+      pollStatus userID token session jobID >>= \case
+        Just error -> return $ Left $ error
+        Nothing -> do 
+          let transcriptUri = jobsUri userID ((show jobID) `slash` "transcript" <> (tokenUri token))
+          result <- Sess.getWith makeOpts session transcriptUri
+          return $ first ParseError $ eitherDecode (result ^. responseBody)
+  where 
+    auth = (withAuthorization options)
+    postUri =  jobsUri userID (tokenUri token)
+
+pollStatus :: UserID -> AuthToken -> Sess.Session -> JobID -> IO(Maybe Error)
+pollStatus userID token session jobID = pollStatus' Nothing userID token session jobID
+
+waitFor :: Int -> IO()
+waitFor x = threadDelay (1000000*x)
+
+pollStatus' :: Maybe Integer -> UserID -> AuthToken -> Sess.Session -> JobID -> IO(Maybe Error)
+pollStatus' (Just wait) user auth sess jobid = 
+  waitFor (fromIntegral wait) >> (pollStatus' Nothing) user auth sess jobid
+pollStatus' Nothing userID token session jobID = do 
+  let statusUri = (jobsUri userID $ show $ jobID) <> (tokenUri token)
+  statusResponse <- Sess.getWith makeOpts session statusUri
+  let body = statusResponse ^. responseBody
+  case second Peek.jobCheckWait (Peek.parse body) of 
+    Left error -> return $ Just $ ParseError error 
+    Right Nothing -> return $ Nothing -- done
+    Right maybe -> pollStatus' maybe userID token session jobID
diff --git a/src/Speechmatics/JSON/PeekJob.hs b/src/Speechmatics/JSON/PeekJob.hs
new file mode 100644
--- /dev/null
+++ b/src/Speechmatics/JSON/PeekJob.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE DeriveGeneric       #-}
+
+module Speechmatics.JSON.PeekJob where
+
+import           System.Exit        (exitFailure, exitSuccess)
+import           System.IO          (stderr, hPutStrLn)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           System.Environment (getArgs)
+import           Control.Monad      (forM_, mzero, join)
+import           Control.Applicative
+import           Data.Aeson.AutoType.Alternative
+import           Data.Aeson(decode, Value(..), FromJSON(..), ToJSON(..),
+                            pairs, eitherDecode ,
+                            (.:), (.:?), (.=), object)
+import           Data.Monoid
+import           Data.Text (Text)
+import Data.Bifunctor
+import qualified GHC.Generics
+
+-- | Workaround for https://github.com/bos/aeson/issues/287.
+o .:?? val = fmap join (o .:? val)
+
+data Job = Job { 
+    jobCheckWait :: (Maybe Integer),
+    jobNotification :: Text,
+    jobJobType :: Text,
+    jobUrl :: Text,
+    jobNextCheck :: Double,
+    jobLang :: Text,
+    jobJobStatus :: Text,
+    jobName :: Text,
+    jobCreatedAt :: Text,
+    jobId :: Double,
+    jobMeta :: (Maybe Value),
+    jobUserId :: Double,
+    jobDuration :: Double,
+    jobTranscription :: Maybe Text
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON Job where
+  parseJSON (Object v) = Job <$> v .:?? "check_wait" <*> v .:   "notification" <*> v .:   "job_type" <*> v .:   "url" <*> v .:   "next_check" <*> v .:   "lang" <*> v .:   "job_status" <*> v .:   "name" <*> v .:   "created_at" <*> v .:   "id" <*> v .:?? "meta" <*> v .:   "user_id" <*> v .:   "duration" <*> v .:   "transcription"
+  parseJSON _          = mzero
+
+
+data TopLevel = TopLevel { 
+    topLevelJob :: Job
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON TopLevel where
+  parseJSON (Object v) = TopLevel <$> v .:   "job"
+  parseJSON _          = mzero
+
+parse :: BSL.ByteString -> Either String Job
+parse = (second topLevelJob)  -- get rid of toplevel
+          . eitherDecode 
diff --git a/src/Speechmatics/JSON/PostJob.hs b/src/Speechmatics/JSON/PostJob.hs
new file mode 100644
--- /dev/null
+++ b/src/Speechmatics/JSON/PostJob.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE DeriveGeneric       #-}
+
+module Speechmatics.JSON.PostJob where
+
+import           System.Exit        (exitFailure, exitSuccess)
+import           System.IO          (stderr, hPutStrLn)
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import           System.Environment (getArgs)
+import           Control.Monad      (forM_, mzero, join)
+import           Control.Applicative
+import           Data.Aeson.AutoType.Alternative
+import           Data.Aeson(decode, Value(..), FromJSON(..), ToJSON(..),
+                            pairs, eitherDecode,
+                            (.:), (.:?), 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 PostJob = PostJob { 
+    checkWait :: (Maybe Value),
+    cost :: Double,
+    balance :: Double,
+    postId :: Integer
+  } deriving (Show,Eq,GHC.Generics.Generic)
+
+
+instance FromJSON PostJob where
+  parseJSON (Object v) = PostJob <$> v .:?? "check_wait" <*> v .:   "cost" <*> v .:   "balance" <*> v .:   "id"
+  parseJSON _          = mzero
+
+parse :: BSL.ByteString -> Either String PostJob
+parse = eitherDecode
diff --git a/test/PeekJobSpec.hs b/test/PeekJobSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/PeekJobSpec.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module PeekJobSpec (spec) where
+
+import NeatInterpolation (text)
+import           Speechmatics.JSON.PeekJob
+import           Test.Hspec
+import Data.Text.Encoding(encodeUtf8)
+import Data.ByteString.Lazy
+
+
+progressExpected :: Job
+progressExpected = Job {
+  jobCheckWait = Just 30,
+  jobNotification = "email",
+  jobJobType = "transcription",
+  jobUrl = "/v1.0/user/42578/jobs/8221819/audio",
+  jobNextCheck = 1.527572657e9,
+  jobLang = "en-US",
+  jobJobStatus = "transcribing",
+  jobName = "file.mp3",
+  jobCreatedAt = "Tue, 29 May 2018 05:43:46 GMT",
+  jobId = 8221819.0,
+  jobMeta = Nothing,
+  jobUserId = 42578.0,
+  jobDuration = 4.0,
+  jobTranscription = Nothing
+}
+
+progressFixture :: ByteString
+progressFixture = fromStrict $ encodeUtf8 [text|
+{
+  "job": {
+    "check_wait": 30, 
+    "created_at": "Tue, 29 May 2018 05:43:46 GMT", 
+    "duration": 4, 
+    "id": 8221819, 
+    "job_status": "transcribing", 
+    "job_type": "transcription", 
+    "lang": "en-US", 
+    "meta": null, 
+    "name": "file.mp3", 
+    "next_check": 1527572657, 
+    "notification": "email", 
+    "transcription": null, 
+    "url": "/v1.0/user/42578/jobs/8221819/audio", 
+    "user_id": 42578
+  }
+}
+|]
+
+doneExpected :: Job
+doneExpected = Job {
+  jobCheckWait = Nothing,
+  jobNotification = "email",
+  jobJobType = "transcription",
+  jobUrl = "/v1.0/user/42578/jobs/8220130/audio",
+  jobNextCheck = 0.0,
+  jobLang = "en-US",
+  jobJobStatus = "done",
+  jobName = "zero.wav",
+  jobCreatedAt = "Tue, 29 May 2018 04:21:48 GMT",
+  jobId = 8220130.0,
+  jobMeta = Nothing,
+  jobUserId = 42578.0,
+  jobDuration = 0.0,
+  jobTranscription = Just "zero.json"
+}
+doneFixture :: ByteString
+doneFixture = fromStrict $ encodeUtf8 [text|
+{
+  "job": {
+    "check_wait": null, 
+    "created_at": "Tue, 29 May 2018 04:21:48 GMT", "duration": 0, 
+    "id": 8220130, 
+    "job_status": "done", 
+    "job_type": "transcription", 
+    "lang": "en-US", 
+    "meta": null, 
+    "name": "zero.wav", 
+    "next_check": 0, 
+    "notification": "email", 
+    "transcription": "zero.json", 
+    "url": "/v1.0/user/42578/jobs/8220130/audio", 
+    "user_id": 42578
+  }
+}
+|]
+
+spec :: Spec
+spec = do
+  describe "Ceres stats output parser" $ do
+    it "parses doneFixture to doneExpected value" $ do
+      (parse doneFixture) `shouldBe` (Right doneExpected)
+    it "parses progressFixture to doneExpected value" $ do
+      (parse progressFixture) `shouldBe` (Right progressExpected)
+    it "does not parse invalid json" $ do
+      (parse "not json") `shouldSatisfy` (\case 
+                                             Left _ -> True
+                                             _ -> False)
diff --git a/test/PostJobSpec.hs b/test/PostJobSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/PostJobSpec.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+module PostJobSpec (spec) where
+
+import NeatInterpolation (text)
+import           Speechmatics.JSON.PostJob
+import           Test.Hspec
+import Data.Text.Encoding(encodeUtf8)
+import Data.ByteString.Lazy
+
+expected :: PostJob
+expected = PostJob {
+    checkWait = Nothing,
+    cost = 0.0,
+    balance = 9195.0,
+    postId = 8217275
+  }
+
+fixture :: ByteString
+fixture = fromStrict $ encodeUtf8 [text|
+{
+  "balance": 9195, 
+  "check_wait": null, 
+  "cost": 0, 
+  "id": 8217275
+}|]
+
+spec :: Spec
+spec = do
+  describe "Ceres stats output parser" $ do
+    it "parses fixture to expected value" $ do
+      (parse fixture) `shouldBe` (Right expected)
+    it "does not parse invalid json" $ do
+      (parse "not json") `shouldSatisfy` (\case 
+                                             Left _ -> True
+                                             _ -> False)
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
