diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,10 +2,14 @@
 
 [![Build Status](https://travis-ci.org/aviaviavi/curl-runnings.svg?branch=master)](https://travis-ci.org/aviaviavi/curl-runnings)
 
-Feel the rhythm! Feel the rhyme! Get on up, it's testing time! curl-runnings!
+_Feel the rhythm! Feel the rhyme! Get on up, it's testing time! curl-runnings!_
 
 curl-runnings is a framework for writing declarative, curl based tests for your APIs. 
 
+Write your tests quickly and correctly with a straight-forward specification in
+yaml or json. A DSL for writing your tests is on the way! Alternatively, you can
+use the curl-runnings library to write your tests directly in Haskell.
+
 ### Why?
 
 When writing curl based smoke/integration tests for APIs using bash and `curl`
@@ -27,68 +31,16 @@
 
 There are few options to install:
 
-- clone this repo and run `stack install`
-- download the releases 
-- (soon) - `cabal install curl-runnings`
+- download the releases from the github [releases page](https://github.com/aviaviavi/curl-runnings/releases)
+- `stack install curl-runnings`
+- `cabal install curl-runnings`
+- build from source with `stack`
 
 ### Writing a test specification
 
-A test spec is a top level list of test cases, each item represents a single curl and set of assertions about the response:
-
-```yaml
----
-# the top level of the file is an array of test cases
-- name: test 1 # required
-  url: http://your-endpoint.com # required
-  requestMethod: GET # required
-  # specify the json payload we expect here, if any
-  expectData: # optional
-    # `tag` refers to the type of matcher we want to use
-    # valid tags are `Exactly` | `Contains`
-    tag: Exactly
-    # check for exactly this payload
-    contents:
-      okay: true
-      msg: ''
-  expectStatus: # requried
-    # `tag` refers to the type of matcher we want to use
-    # valid tags are `ExactCode` (contents :: number) | `AnyCodeIn` (contents :: [number])
-    tag: ExactCode
-    contents: 200
-- name: test 2
-  url: http://your-endpoint.com/path
-  requestMethod: POST
-  expectStatus:
-    # Any code listed in `contents` is valid
-    tag: AnyCodeIn
-    contents:
-    - 200
-    - 201
-  # json data to send with the request
-  requestData:
-    hello: there
-    num: 1
-- name: test 3
-  url: http://your-url.com/other/path
-  requestMethod: GET
-  expectData:
-    # apply a list of matchers to the returned json payload
-    tag: Contains
-    contents:
-    # valid tags are `ValueMatch` | `KeyValueMatch`
-    # find the value `true` anywhere in the payload. This can be useful for matching against values 
-    # where you don't know the key ahead of time, or for values in a top level array.
-    - tag: ValueMatch
-      contents: true
-    # `KeyValueMatch` looks for the key/value pair anywhere in the payload
-    # here, {'okay': true} must be somewhere in the return payload
-    - tag: KeyValueMatch
-      matchKey: okay
-      matchValue: true
-  expectStatus:
-    tag: ExactCode
-    contents: 200
-```
+For now, you write your tests specs in a yaml or json file. See /examples to get
+started. A test spec is a top level array of test cases, each item represents a
+single curl and set of assertions about the response.
 
 ### Running
 
@@ -107,8 +59,9 @@
 $ curl-runnings --help
 ```
 
+### Roadmap
 
-### Future work
+Contributions in any form are welcome and encouraged. Don't be shy! :D
 
 - [x] Json specifications for tests
 - [x] Yaml specifications for tests
@@ -119,3 +72,4 @@
   - [ ] timeouts
   - [ ] retry logic
   - [ ] ability to configure alerts
+  
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -3,12 +3,8 @@
 
 module Main where
 
-import           Data.Aeson
-import qualified Data.ByteString.Char8           as B8S
-import qualified Data.ByteString.Lazy            as B
 import qualified Data.Text                       as T
 import           Data.Version                    (showVersion)
-import qualified Data.Yaml                       as Y
 import           Paths_curl_runnings             (version)
 import           System.Console.CmdArgs
 import           System.Environment
@@ -30,22 +26,6 @@
   summary ("curl-runnings " ++ showVersion version) &=
   program "curl-runnings" &=
   help "Use the --file or -f flag to specify an intput file spec to run"
-
--- | decode a json or yaml file into a suite object
-decodeFile :: FilePath -> IO (Either String CurlSuite)
-decodeFile specPath =
-  case last $ T.splitOn "." (T.pack specPath) of
-    "json" ->
-      eitherDecode' <$> B.readFile specPath :: IO (Either String CurlSuite)
-    "yaml" ->
-      Y.decodeEither <$> B8S.readFile specPath :: IO (Either String CurlSuite)
-    "yml" ->
-      Y.decodeEither <$> B8S.readFile specPath :: IO (Either String CurlSuite)
-    _ ->
-      return . Left $
-      printf
-        "Invalid spec path %s"
-        (T.pack specPath)
 
 main :: IO ()
 main = do
diff --git a/curl-runnings.cabal b/curl-runnings.cabal
--- a/curl-runnings.cabal
+++ b/curl-runnings.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: d8d3fa8a875ec9e27b6db4c1ad13dcc50d7354bbbc647ae07ff08a91b0f4c79b
+-- hash: 5ba21bafee5c2e642cec1c0c7bff81b86f715d883cc524542a61d787f7aca778
 
 name:           curl-runnings
-version:        0.1.0
+version:        0.2.0
 synopsis:       A framework for declaratively writing curl based API tests
 description:    Please see the README on Github at <https://github.com/aviaviavi/curl-runnings#readme>
 category:       Testing
@@ -20,6 +20,9 @@
 cabal-version:  >= 1.10
 
 extra-source-files:
+    examples/example-spec.json
+    examples/example-spec.yaml
+    examples/test.json
     README.md
 
 source-repository head
@@ -30,13 +33,17 @@
   hs-source-dirs:
       src
   build-depends:
-      aeson >=1.2.4.0 && <1.3
-    , aeson-pretty >=0.8.5 && <0.9
+      aeson >=1.2.4.0
+    , aeson-pretty >=0.8.5
     , base >=4.7 && <5
-    , bytestring >=0.10.8.2 && <0.11
-    , http-conduit >=2.2.4 && <2.3
-    , text >=1.2.2.2 && <1.3
-    , unordered-containers >=0.2.8.0 && <0.3
+    , bytestring >=0.10.8.2
+    , directory >=1.3.0.2
+    , hspec >=2.4.4
+    , hspec-expectations >=0.8.2
+    , http-conduit >=2.2.4
+    , text >=1.2.2.2
+    , unordered-containers >=0.2.8.0
+    , yaml >=0.8.28
   exposed-modules:
       Testing.CurlRunnings
       Testing.CurlRunnings.Types
@@ -51,13 +58,10 @@
       app
   ghc-options: -threaded -rtsopts -with-rtsopts=-N
   build-depends:
-      aeson >=1.2.4.0 && <1.3
-    , base >=4.7 && <5
-    , bytestring >=0.10.8.2 && <0.11
-    , cmdargs >=0.10.20 && <0.11
+      base >=4.7
+    , cmdargs >=0.10.20
     , curl-runnings
-    , text >=1.2.2.2 && <1.3
-    , yaml >=0.8.28 && <0.9
+    , text >=1.2.2.2
   other-modules:
       Paths_curl_runnings
   default-language: Haskell2010
@@ -71,6 +75,9 @@
   build-depends:
       base >=4.7 && <5
     , curl-runnings
+    , directory >=1.3.0.2
+    , hspec >=2.4.4
+    , hspec-expectations >=0.8.2
   other-modules:
       Paths_curl_runnings
   default-language: Haskell2010
diff --git a/examples/example-spec.json b/examples/example-spec.json
new file mode 100644
--- /dev/null
+++ b/examples/example-spec.json
@@ -0,0 +1,46 @@
+[
+  {
+    "name": "test 1",
+    "url": "http://your-endpoint.com/status",
+    "requestMethod": "GET",
+    "expectData": {
+      "exactly": {
+        "okay": true,
+        "msg": "a message"
+      }
+    },
+    "expectStatus": 200
+  },
+  {
+    "name": "test 2",
+    "url": "http://your-endpoint.com/path",
+    "requestMethod": "POST",
+    "expectStatus": [
+      200,
+      201
+    ],
+    "requestData": {
+      "hello": "there",
+      "num": 1
+    }
+  },
+  {
+    "name": "test 3",
+    "url": "http://your-url.com/other/path",
+    "requestMethod": "GET",
+    "expectData": {
+      "contains": [
+        {
+          "keyValueMatch": {
+            "key": "okay",
+            "value": true
+          }
+        },
+        {
+          "valueMatch": true
+        }
+      ]
+    },
+    "expectStatus": 200
+  }
+]
diff --git a/examples/example-spec.yaml b/examples/example-spec.yaml
new file mode 100644
--- /dev/null
+++ b/examples/example-spec.yaml
@@ -0,0 +1,46 @@
+---
+# The top level of the file is an array of test cases
+- name: test 1 # required
+  url: http://your-endpoint.com/status # required
+  requestMethod: GET # required
+  # [Optional] Specify the json payload we expect here
+  # The 1 key in this block should be either:
+  # exactly | contains
+  expectData:
+    # The 1 key in this object specifies the matcher we want
+    # to use to test the returned payload. In this case, we
+    # require the payload is exactly what we specify.
+    exactly:
+      okay: true
+      msg: 'a message'
+  # [Required] Assertions about the returned status code. Pass in
+  # an acceptable code or list of codes
+  expectStatus: 200
+- name: test 2
+  url: http://your-endpoint.com/path
+  requestMethod: POST
+  expectStatus:
+  - 200
+  - 201
+  # [Optional] json data to send with the request
+  requestData:
+    hello: there
+    num: 1
+- name: test 3
+  url: http://your-url.com/other/path
+  requestMethod: GET
+  expectData:
+    # In the `contains` case of data validation, a list of matchers is specified. Currently,
+    # possible types are `valueMatch` | `keyValueMatch`.
+    contains:
+    # `keyValueMatch` looks for the key/value pair anywhere in the payload
+    # here, {'okay': true} must be somewhere in the return payload
+    - keyValueMatch:
+        key: okay
+        value: true
+    # `valueMatch` searches for a value anywhere in the payload (note: _not_ a key).
+    # Here, we look for the value `true` anywhere in the payload.
+    # This can be useful for matching against values where you don't know the key ahead of time,
+    # or for values in a top level array.
+    - valueMatch: true
+  expectStatus: 200
diff --git a/examples/test.json b/examples/test.json
new file mode 100644
--- /dev/null
+++ b/examples/test.json
@@ -0,0 +1,75 @@
+[
+  {
+    "name": "test 1",
+    "url": "http://sd.ddpay.io/status",
+    "requestMethod": "GET",
+    "expectData": {
+        "tag": "Exactly",
+        "contents": {
+            "okay": true,
+            "msg": ""
+        }
+    },
+    "expectStatus": {
+        "tag": "ExactCode",
+        "contents": 200
+    },
+    "requestData": null
+  },
+
+  {
+    "name": "test 2",
+    "url": "http://sd.ddpay.io/status",
+    "requestMethod": "GET",
+    "expectData": {
+        "tag": "Contains",
+        "contents": [
+          {
+            "tag": "ValueMatch",
+            "contents": true
+          },
+          {
+            "tag": "ValueMatch",
+            "contents": ""
+          },
+          {
+            "tag": "KeyValueMatch",
+            "matchKey": "msg",
+            "matchValue": ""
+          },
+          {
+            "tag": "KeyValueMatch",
+            "matchKey": "okay",
+            "matchValue": true
+          }
+                      ]
+    },
+    "expectStatus": {
+      "tag": "AnyCodeIn",
+      "contents": [200, 300]
+    },
+    "requestData": null
+  },
+
+  {
+    "name": "test 4",
+    "url": "http://sd.ddpay.io/v1/DevOnlyGetAllCustomerIds",
+    "requestMethod": "POST",
+    "expectStatus": {
+      "tag": "ExactCode",
+      "contents": 400
+    },
+    "requestData": {}
+  },
+  {
+    "name": "test 5",
+    "url": "http://sd.ddpay.io/v1/DevOnlyGetAllCustomerIds",
+    "requestMethod": "POST",
+    "expectData": null,
+    "expectStatus": {
+      "tag": "AnyCodeIn",
+      "contents": [401]
+    },
+    "requestData": {"api_token": "asdf"}
+  }
+]
diff --git a/src/Testing/CurlRunnings.hs b/src/Testing/CurlRunnings.hs
--- a/src/Testing/CurlRunnings.hs
+++ b/src/Testing/CurlRunnings.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 -- | curl-runnings is a framework for writing declaratively writing curl based tests for your API's.
 -- Write your test specifications with yaml or json, and you're done!
 --
@@ -5,6 +7,7 @@
     (
       runCase
     , runSuite
+    , decodeFile
     ) where
 
 import           Control.Monad
@@ -13,11 +16,25 @@
 import qualified Data.ByteString.Lazy       as B
 import qualified Data.HashMap.Strict        as H
 import           Data.Maybe
-import qualified Data.Text                  as T
+import qualified Data.Text as T
+import qualified Data.Yaml                  as Y
 import           Network.HTTP.Conduit
 import           Network.HTTP.Simple
 import           Testing.CurlRunnings.Types
+import           Text.Printf
 
+-- | decode a json or yaml file into a suite object
+decodeFile :: FilePath -> IO (Either String CurlSuite)
+decodeFile specPath =
+  case last $ T.splitOn "." (T.pack specPath) of
+    "json" ->
+      eitherDecode' <$> B.readFile specPath :: IO (Either String CurlSuite)
+    "yaml" ->
+      Y.decodeEither <$> B8S.readFile specPath :: IO (Either String CurlSuite)
+    "yml" ->
+      Y.decodeEither <$> B8S.readFile specPath :: IO (Either String CurlSuite)
+    _ -> return . Left $ printf "Invalid spec path %s" (T.pack specPath)
+
 -- | Run a single test case, and returns the result. IO is needed here since this method is responsible
 -- for actually curling the test case endpoint and parsing the result.
 runCase :: CurlCase -> IO CaseResult
@@ -79,11 +96,10 @@
 -- | Does the json value contain all of these sub-values?
 jsonContainsAll :: Value -> [JsonSubExpr] -> Bool
 jsonContainsAll jsonValue =
-  all (\match -> case match  of
+  all $ \match -> case match  of
           ValueMatch subval -> subval `elem` traverseValue jsonValue
           KeyValueMatch key subval ->
             containsKeyVal jsonValue key subval
-      )
 
 -- | Does the json value contain the given key value pair?
 containsKeyVal :: Value -> T.Text -> Value -> Bool
diff --git a/src/Testing/CurlRunnings/Types.hs b/src/Testing/CurlRunnings/Types.hs
--- a/src/Testing/CurlRunnings/Types.hs
+++ b/src/Testing/CurlRunnings/Types.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 -- | Data types for curl-runnings tests
@@ -15,13 +15,16 @@
   , isFailing
   ) where
 
-import Data.Aeson
-import Data.Aeson.Encode.Pretty
-import qualified Data.ByteString.Lazy.Char8 as B8
-import qualified Data.Text as T
-import GHC.Generics
-import Text.Printf
-import Testing.CurlRunnings.Internal
+import           Data.Aeson
+import           Data.Aeson.Encode.Pretty
+import           Data.Aeson.Types
+import qualified Data.ByteString.Lazy.Char8    as B8
+import qualified Data.HashMap.Strict           as H
+import           Data.Maybe
+import qualified Data.Text                     as T
+import           GHC.Generics
+import           Testing.CurlRunnings.Internal
+import           Text.Printf
 
 -- | A basic enum for supported HTTP verbs
 data HttpMethod
@@ -42,13 +45,16 @@
   = Exactly Value
   -- | A list of matchers to make assertions about some subset of the response.
   | Contains [JsonSubExpr]
-
   deriving (Show, Generic)
 
-instance FromJSON JsonMatcher
-
 instance ToJSON JsonMatcher
 
+instance FromJSON JsonMatcher where
+  parseJSON (Object v)
+    | isJust $ H.lookup "exactly" v = Exactly <$> v .: "exactly"
+    | isJust $ H.lookup "contains" v = Contains <$> v .: "contains"
+  parseJSON invalid = typeMismatch "JsonMatcher" invalid
+
 -- | A matcher for a subvalue of a json payload
 data JsonSubExpr
   -- | Assert some value anywhere in the json has a value equal to a given
@@ -56,12 +62,19 @@
   --  top level array. It's also useful if you don't know the key ahead of time.
   = ValueMatch Value
   -- | Assert the key value pair can be found somewhere the json.
-  | KeyValueMatch { matchKey :: T.Text
+  | KeyValueMatch { matchKey   :: T.Text
                   , matchValue :: Value }
   deriving (Show, Generic)
 
-instance FromJSON JsonSubExpr
-
+instance FromJSON JsonSubExpr where
+  parseJSON (Object v)
+    | isJust $ H.lookup "keyValueMatch" v =
+      let toParse = fromJust $ H.lookup "keyValueMatch" v
+      in case toParse of
+           Object o -> KeyValueMatch <$> o .: "key" <*> o .: "value"
+           _        -> typeMismatch "JsonSubExpr" toParse
+    | isJust $ H.lookup "valueMatch" v = ValueMatch <$> v .: "valueMatch"
+  parseJSON invalid = typeMismatch "JsonSubExpr" invalid
 instance ToJSON JsonSubExpr
 
 -- | Check the status code of a response. You can specify one or many valid codes.
@@ -70,18 +83,21 @@
   | AnyCodeIn [Int]
   deriving (Show, Generic)
 
-instance FromJSON StatusCodeMatcher
-
 instance ToJSON StatusCodeMatcher
 
+instance FromJSON StatusCodeMatcher where
+  parseJSON obj@(Number _) = ExactCode <$> parseJSON obj
+  parseJSON obj@(Array _)  = AnyCodeIn <$> parseJSON obj
+  parseJSON invalid        = typeMismatch "StatusCodeMatcher" invalid
+
 -- | A single curl test case, the basic foundation of a curl-runnings test.
 data CurlCase = CurlCase
-  { name :: String -- ^ The name of the test case
-  , url :: String -- ^ The target url to test
+  { name          :: String -- ^ The name of the test case
+  , url           :: String -- ^ The target url to test
   , requestMethod :: HttpMethod -- ^ Berb to use for the request
-  , requestData :: Maybe Value -- ^ Payload to send with the request, if any
-  , expectData :: Maybe JsonMatcher -- ^ The assertions to make on the response payload, if any
-  , expectStatus :: StatusCodeMatcher -- ^ Assertion about the status code returned by the target
+  , requestData   :: Maybe Value -- ^ Payload to send with the request, if any
+  , expectData    :: Maybe JsonMatcher -- ^ The assertions to make on the response payload, if any
+  , expectStatus  :: StatusCodeMatcher -- ^ Assertion about the status code returned by the target
   } deriving (Show, Generic)
 
 instance FromJSON CurlCase
@@ -162,10 +178,10 @@
 
 -- | Simple predicate that checks if the result is passing
 isPassing :: CaseResult -> Bool
-isPassing (CasePass _) = True
+isPassing (CasePass _)   = True
 isPassing (CaseFail _ _) = False
 
 -- | Simple predicate that checks if the result is failing
 isFailing :: CaseResult -> Bool
-isFailing (CasePass _) = False
+isFailing (CasePass _)   = False
 isFailing (CaseFail _ _) = True
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,2 +1,26 @@
+module Main where
+
+import           System.Directory
+import           Test.Hspec
+import           Testing.CurlRunnings
+
+isRight :: Either a b -> Bool
+isRight (Right _) = True
+isRight _         = False
+
 main :: IO ()
-main = putStrLn "Test suite not yet implemented"
+main = hspec $
+  describe "Curl Runnings" $ do
+  it "should provide valid example yaml specs" $
+    testValidSpec "/examples/example-spec.yaml"
+
+  it "should provide valid example json specs" $
+    testValidSpec "/examples/example-spec.json"
+
+testValidSpec :: String -> IO ()
+testValidSpec file = do
+  currentDirectory <- getCurrentDirectory
+  spec <- decodeFile (currentDirectory ++ file)
+  spec `shouldSatisfy` isRight
+
+
