diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,10 @@
+# Unreleased
+
+# 0.5.0.0
+* Add `Num Port`.
+* Add json interpreters that transform payloads.
+* Change pure `Http` interpreter to use supplied responses for streaming requests.
+
 # 0.4.0.0
 * Use `CookieJar` in the `Response` instead of requiring users to extract them from the headers.
 
diff --git a/integration/Polysemy/Http/CookieTest.hs b/integration/Polysemy/Http/CookieTest.hs
--- a/integration/Polysemy/Http/CookieTest.hs
+++ b/integration/Polysemy/Http/CookieTest.hs
@@ -6,13 +6,13 @@
 import Polysemy.Log (interpretLogNull)
 import Polysemy.Resource (resourceToIOFinal)
 
-import qualified Polysemy.Http.Data.Http as Http
+import qualified Polysemy.Http.Effect.Http as Http
 import Polysemy.Http.Data.HttpError (HttpError)
 import qualified Polysemy.Http.Data.Request as Request
 import Polysemy.Http.Data.Request (Port(Port), Tls(Tls))
 import qualified Polysemy.Http.Data.Response as Response
 import Polysemy.Http.Data.Response (Response)
-import Polysemy.Http.Native (interpretHttpNative)
+import Polysemy.Http.Interpreter.Native (interpretHttpNative)
 import qualified Polysemy.Http.Request as Request
 import Polysemy.Http.Request (addCookie)
 import Polysemy.Http.Server (withServer)
diff --git a/integration/Polysemy/Http/RequestTest.hs b/integration/Polysemy/Http/RequestTest.hs
--- a/integration/Polysemy/Http/RequestTest.hs
+++ b/integration/Polysemy/Http/RequestTest.hs
@@ -7,14 +7,14 @@
 import Polysemy.Log (interpretLogNull)
 import Polysemy.Resource (resourceToIOFinal)
 
-import qualified Polysemy.Http.Data.Http as Http
+import qualified Polysemy.Http.Effect.Http as Http
 import Polysemy.Http.Data.HttpError (HttpError)
 import qualified Polysemy.Http.Data.Request as Request
 import Polysemy.Http.Data.Request (Body(Body), Port(Port), Tls(Tls))
 import qualified Polysemy.Http.Data.Response as Response
 import Polysemy.Http.Data.Response (Response)
 import Polysemy.Http.Json (jsonContentType)
-import Polysemy.Http.Native (interpretHttpNative)
+import Polysemy.Http.Interpreter.Native (interpretHttpNative)
 import qualified Polysemy.Http.Request as Request
 import Polysemy.Http.Server (Payload(Payload), withServer)
 import Polysemy.Http.Test (UnitTest)
diff --git a/integration/Polysemy/Http/StreamTest.hs b/integration/Polysemy/Http/StreamTest.hs
--- a/integration/Polysemy/Http/StreamTest.hs
+++ b/integration/Polysemy/Http/StreamTest.hs
@@ -10,12 +10,12 @@
 
 import Polysemy.Http.Data.HttpError (HttpError)
 import qualified Polysemy.Http.Data.Request as Request
-import Polysemy.Http.Data.Request (Port(Port), Request, Tls(Tls))
-import Polysemy.Http.Data.StreamChunk (StreamChunk(StreamChunk))
+import Polysemy.Http.Data.Request (Port (Port), Request, Tls (Tls))
+import Polysemy.Http.Data.StreamChunk (StreamChunk (StreamChunk))
 import qualified Polysemy.Http.Data.StreamEvent as StreamEvent
 import Polysemy.Http.Data.StreamEvent (StreamEvent)
 import qualified Polysemy.Http.Http as Http
-import Polysemy.Http.Native (interpretHttpNative)
+import Polysemy.Http.Interpreter.Native (interpretHttpNative)
 import qualified Polysemy.Http.Request as Request
 import Polysemy.Http.Server (withServer)
 import Polysemy.Http.Test (UnitTest)
diff --git a/lib/Polysemy/Http.hs b/lib/Polysemy/Http.hs
--- a/lib/Polysemy/Http.hs
+++ b/lib/Polysemy/Http.hs
@@ -1,9 +1,12 @@
+-- |Description: Polysemy Effects for HTTP clients
 module Polysemy.Http (
   -- $intro
-  module Polysemy.Http.Data.Http,
+  module Polysemy.Http.Effect.Http,
+
   -- * Interpreters
-  module Polysemy.Http.Native,
-  module Polysemy.Http.Strict,
+  module Polysemy.Http.Interpreter.Native,
+  module Polysemy.Http.Interpreter.Pure,
+
   -- * Request and Response
   module Polysemy.Http.Data.Request,
   module Polysemy.Http.Data.Response,
@@ -24,9 +27,11 @@
   addCookies,
   addCookie,
   HttpError(..),
+
   -- * Streaming
   module Polysemy.Http.Http,
   module Polysemy.Http.Data.StreamEvent,
+
   -- * Entity
   EntityDecode,
   decode,
@@ -40,16 +45,40 @@
   Decoders,
   Encoders,
   EntityError(EntityError),
-  module Polysemy.Http.AesonEntity,
+  module Polysemy.Http.Interpreter.AesonEntity,
+
   -- * Utilities
   -- ** Connection Pool
-  module Polysemy.Http.Data.Manager,
+  module Polysemy.Http.Effect.Manager,
+  interpretManager,
+  jsonRequest,
 ) where
 
 import Prelude hiding (get, put)
 
-import Polysemy.Http.AesonEntity (interpretEntityDecodeAeson, interpretEntityEncodeAeson)
-import Polysemy.Http.Data.Entity (
+import Polysemy.Http.Data.Header (Header (..), HeaderName (..), HeaderValue (..))
+import Polysemy.Http.Data.HttpError (HttpError (..))
+import Polysemy.Http.Data.Request (
+  Body (..),
+  Host (..),
+  Method (..),
+  Path (..),
+  Port (..),
+  QueryKey (..),
+  QueryValue (..),
+  Request (Request),
+  Tls (..),
+  )
+import Polysemy.Http.Data.Response (
+  pattern Client,
+  pattern Info,
+  pattern Redirect,
+  Response (Response),
+  pattern Server,
+  pattern Success,
+  )
+import Polysemy.Http.Data.StreamEvent (StreamEvent (..))
+import Polysemy.Http.Effect.Entity (
   Decode,
   Decoders,
   Encode,
@@ -57,38 +86,25 @@
   Entities,
   EntityDecode,
   EntityEncode,
-  EntityError(EntityError),
+  EntityError (EntityError),
   decode,
   decodeStrict,
   encode,
   encodeStrict,
   )
-import Polysemy.Http.Data.Header (Header(..), HeaderName(..), HeaderValue(..))
-import Polysemy.Http.Data.Http (Http, request, response, stream)
-import Polysemy.Http.Data.HttpError (HttpError(..))
-import Polysemy.Http.Data.Manager (Manager)
-import Polysemy.Http.Data.Request (
-  Body(..),
-  Host(..),
-  Method(..),
-  Path(..),
-  Port(..),
-  QueryKey(..),
-  QueryValue(..),
-  Request(Request),
-  Tls(..),
-  )
-import Polysemy.Http.Data.Response (
-  Response(Response),
-  pattern Client,
-  pattern Info,
-  pattern Redirect,
-  pattern Server,
-  pattern Success,
-  )
-import Polysemy.Http.Data.StreamEvent (StreamEvent(..))
+import Polysemy.Http.Effect.Http (Http, request, response, stream)
+import Polysemy.Http.Effect.Manager (Manager)
 import Polysemy.Http.Http (streamResponse)
-import Polysemy.Http.Native (interpretHttpNative)
+import Polysemy.Http.Interpreter.AesonEntity (
+  interpretEntityDecodeAeson,
+  interpretEntityDecodeAesonAs,
+  interpretEntityEncodeAeson,
+  interpretEntityEncodeAesonAs,
+  )
+import Polysemy.Http.Interpreter.Manager (interpretManager)
+import Polysemy.Http.Interpreter.Native (interpretHttpNative)
+import Polysemy.Http.Interpreter.Pure (interpretHttpPure)
+import Polysemy.Http.Json (jsonRequest)
 import Polysemy.Http.Request (
   addCookie,
   addCookies,
@@ -106,14 +122,13 @@
   withPort,
   withTls,
   )
-import Polysemy.Http.Strict (interpretHttpStrict)
 
 -- $intro
 -- A basic 'Polysemy' effect abstracting HTTP requests:
 --
 -- @
 -- import Polysemy (resourceToIO, runM)
--- import Polysemy.Log.Colog (interpretLogStdout)
+-- import Polysemy.Log (interpretLogStdout)
 -- import qualified Polysemy.Http as Http
 -- import Polysemy.Http (interpretHttpNative, interpretLogStdout)
 --
diff --git a/lib/Polysemy/Http/AesonEntity.hs b/lib/Polysemy/Http/AesonEntity.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/AesonEntity.hs
+++ /dev/null
@@ -1,41 +0,0 @@
-module Polysemy.Http.AesonEntity where
-
-import Data.Aeson (eitherDecode', eitherDecodeStrict', encode)
-
-import Polysemy.Http.Data.Entity (EntityDecode, EntityEncode, EntityError(EntityError))
-import qualified Polysemy.Http.Data.Entity as Entity (EntityDecode(..), EntityEncode(..))
-
--- |Interpreter for 'EntityEncode' that uses Aeson.
-interpretEntityEncodeAeson ::
-  ToJSON d =>
-  Sem (EntityEncode d : r) a ->
-  Sem r a
-interpretEntityEncodeAeson =
-  interpret \case
-    Entity.Encode a ->
-      pure (encode a)
-    Entity.EncodeStrict a ->
-      pure (toStrict (encode a))
-{-# INLINE interpretEntityEncodeAeson #-}
-
-decodeWith ::
-  ConvertUtf8 Text s =>
-  (s -> Either String a) ->
-  s ->
-  Sem r (Either EntityError a)
-decodeWith dec body =
-  pure . mapLeft (EntityError (decodeUtf8 body) . toText) $ dec body
-{-# INLINE decodeWith #-}
-
--- |Interpreter for 'EntityDecode' that uses Aeson.
-interpretEntityDecodeAeson ::
-  FromJSON d =>
-  Sem (EntityDecode d : r) a ->
-  Sem r a
-interpretEntityDecodeAeson =
-  interpret \case
-    Entity.Decode body ->
-      decodeWith eitherDecode' body
-    Entity.DecodeStrict body ->
-      decodeWith eitherDecodeStrict' body
-{-# INLINE interpretEntityDecodeAeson #-}
diff --git a/lib/Polysemy/Http/Data/Entity.hs b/lib/Polysemy/Http/Data/Entity.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Data/Entity.hs
+++ /dev/null
@@ -1,87 +0,0 @@
-module Polysemy.Http.Data.Entity where
-
-import Polysemy (makeSem_)
-
--- |Generic error type for decoders.
-data EntityError =
-  EntityError {
-    body :: Text,
-    message :: Text
-  }
-  deriving (Eq, Show)
-
--- |Abstraction of json encoding, potentially usable for other content types like xml.
-data EntityEncode d :: Effect where
-  Encode :: d -> EntityEncode d m LByteString
-  EncodeStrict :: d -> EntityEncode d m ByteString
-
-makeSem_ ''EntityEncode
-
--- |Lazily encode a value of type @d@ to a 'LByteString'
-encode ::
-  ∀ d r .
-  Member (EntityEncode d) r =>
-  d ->
-  Sem r LByteString
-
--- |Strictly encode a value of type @d@ to a 'ByteString'
-encodeStrict ::
-  ∀ d r .
-  Member (EntityEncode d) r =>
-  d ->
-  Sem r ByteString
-
--- |Abstraction of json decoding, potentially usable for other content types like xml.
-data EntityDecode d :: Effect where
-  Decode :: LByteString -> EntityDecode d m (Either EntityError d)
-  DecodeStrict :: ByteString -> EntityDecode d m (Either EntityError d)
-
-makeSem_ ''EntityDecode
-
--- |Lazily decode a 'LByteString' to a value of type @d@
-decode ::
-  ∀ d r .
-  Member (EntityDecode d) r =>
-  LByteString ->
-  Sem r (Either EntityError d)
-
--- |Strictly decode a 'ByteString' to a value of type @d@
-decodeStrict ::
-  ∀ d r .
-  Member (EntityDecode d) r =>
-  ByteString ->
-  Sem r (Either EntityError d)
-
--- |Marker type to be used with 'Entities'
-data Encode a
-
--- |Marker type to be used with 'Entities'
-data Decode a
-
--- |Convenience constraint for requiring multiple entity effects, to be used like 'Polysemy.Members'.
---
--- @
--- foo :: Entities [Encode Int, Decode Double] r => Sem r ()
--- @
-type family Entities es r :: Constraint where
-  Entities '[] r = ()
-  Entities (Encode d ': ds) r = (Member (EntityEncode d) r, Entities ds r)
-  Entities (Decode d ': ds) r = (Member (EntityDecode d) r, Entities ds r)
-
--- |Convenience constraint for requiring multiple encoders.
---
--- @
--- foo :: Encoders [Int, Double] r => Sem r ()
--- @
-type family Encoders es r :: Constraint where
-  Encoders '[] r = ()
-  Encoders (d ': ds) r = (Member (EntityEncode d) r, Encoders ds r)
-
--- |Convenience constraint for requiring multiple decoders.
---
--- @
--- foo :: Decoders [Int, Double] r => Sem r ()
--- @
-type family Decoders ds r :: Constraint where
-  Decoders '[] r = ()
-  Decoders (d ': ds) r = (Member (EntityDecode d) r, Decoders ds r)
diff --git a/lib/Polysemy/Http/Data/Header.hs b/lib/Polysemy/Http/Data/Header.hs
--- a/lib/Polysemy/Http/Data/Header.hs
+++ b/lib/Polysemy/Http/Data/Header.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: Header Data Types, Internal
 module Polysemy.Http.Data.Header where
 
 -- |The name of a header.
diff --git a/lib/Polysemy/Http/Data/Http.hs b/lib/Polysemy/Http/Data/Http.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Data/Http.hs
+++ /dev/null
@@ -1,48 +0,0 @@
-module Polysemy.Http.Data.Http where
-
-import Polysemy (makeSem_)
-
-import Polysemy.Http.Data.HttpError (HttpError)
-import Polysemy.Http.Data.Request (Request)
-import Polysemy.Http.Data.Response (Response)
-
--- |The main effect for HTTP requests.
--- The parameter @c@ determines the representation of raw chunks.
-data Http c :: Effect where
-  Response :: Request -> (Response c -> m a) -> Http c m (Either HttpError a)
-  Request :: Request -> Http c m (Either HttpError (Response LByteString))
-  Stream :: Request -> (Response c -> m a) -> Http c m (Either HttpError a)
-  -- |Internal action for streaming transfers.
-  ConsumeChunk :: c -> Http c m (Either HttpError ByteString)
-
-makeSem_ ''Http
-
--- |Bracket a higher-order action with a 'Response' that has been opened while its body hasn't been fetched.
-response ::
-  ∀ c r a .
-  Member (Http c) r =>
-  Request ->
-  (Response c -> Sem r a) ->
-  Sem r (Either HttpError a)
-
--- |Synchronously run an HTTP request and return the response.
-request ::
-  ∀ c r .
-  Member (Http c) r =>
-  Request ->
-  Sem r (Either HttpError (Response LByteString))
-
--- |Open a connection without consuming data and pass the response to a handler for custom transmission.
--- The intended purpose is to allow streaming transfers.
-stream ::
-  ∀ c r a .
-  Member (Http c) r =>
-  Request ->
-  (Response c -> Sem r a) ->
-  Sem r (Either HttpError a)
-
-consumeChunk ::
-  ∀ c r .
-  Member (Http c) r =>
-  c ->
-  Sem r (Either HttpError ByteString)
diff --git a/lib/Polysemy/Http/Data/HttpError.hs b/lib/Polysemy/Http/Data/HttpError.hs
--- a/lib/Polysemy/Http/Data/HttpError.hs
+++ b/lib/Polysemy/Http/Data/HttpError.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: HttpError Data Type, Internal
 module Polysemy.Http.Data.HttpError where
 
 -- |Indicates a critical error caused by an exception in the http-client backend.
diff --git a/lib/Polysemy/Http/Data/Manager.hs b/lib/Polysemy/Http/Data/Manager.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Data/Manager.hs
+++ /dev/null
@@ -1,9 +0,0 @@
-module Polysemy.Http.Data.Manager where
-
-import qualified Network.HTTP.Client as HTTP (Manager)
-
--- |This effect abstracts the creation of a 'Manager' in order to allow pool sharing in a flexible way.
-data Manager :: Effect where
-  Get :: Manager m HTTP.Manager
-
-makeSem ''Manager
diff --git a/lib/Polysemy/Http/Data/Request.hs b/lib/Polysemy/Http/Data/Request.hs
--- a/lib/Polysemy/Http/Data/Request.hs
+++ b/lib/Polysemy/Http/Data/Request.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: Request Data Types, Internal
 module Polysemy.Http.Data.Request where
 
 import Control.Lens (makeClassy)
@@ -71,6 +73,7 @@
 newtype Port =
   Port { unPort :: Int }
   deriving (Eq, Show, Generic)
+  deriving newtype (Num, Ord, Enum, Real, Integral, Read)
 
 defaultJson ''Port
 
@@ -115,7 +118,7 @@
   deriving (Eq, Show, Generic)
   deriving newtype (IsString)
 
--- |HTTP request parameters, used by 'Polysemy.Http.Data.Http'.
+-- |HTTP request parameters, used by 'Polysemy.Http.Effect.Http'.
 data Request =
   Request {
     _method :: Method,
diff --git a/lib/Polysemy/Http/Data/Response.hs b/lib/Polysemy/Http/Data/Response.hs
--- a/lib/Polysemy/Http/Data/Response.hs
+++ b/lib/Polysemy/Http/Data/Response.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: Response Data Types, Internal
 module Polysemy.Http.Data.Response (
   module Polysemy.Http.Data.Response,
   Status(Status),
@@ -16,7 +18,7 @@
 
 import Polysemy.Http.Data.Header (Header)
 
--- |The response produced by 'Polysemy.Http.Data.Http'.
+-- |The response produced by 'Polysemy.Http.Effect.Http'.
 data Response b =
   Response {
     -- |Uses the type from 'Network.HTTP' for convenience.
diff --git a/lib/Polysemy/Http/Data/StreamChunk.hs b/lib/Polysemy/Http/Data/StreamChunk.hs
--- a/lib/Polysemy/Http/Data/StreamChunk.hs
+++ b/lib/Polysemy/Http/Data/StreamChunk.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: StreamChunk Data Type, Internal
 module Polysemy.Http.Data.StreamChunk where
 
 -- |A single chunk produced by 'Network.HTTP.Client.BodyReader'.
diff --git a/lib/Polysemy/Http/Data/StreamEvent.hs b/lib/Polysemy/Http/Data/StreamEvent.hs
--- a/lib/Polysemy/Http/Data/StreamEvent.hs
+++ b/lib/Polysemy/Http/Data/StreamEvent.hs
@@ -1,3 +1,5 @@
+{-# options_haddock prune #-}
+-- |Description: StreamEvent Data Type, Internal
 module Polysemy.Http.Data.StreamEvent where
 
 import Polysemy.Http.Data.Response (Response)
diff --git a/lib/Polysemy/Http/Effect/Entity.hs b/lib/Polysemy/Http/Effect/Entity.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Effect/Entity.hs
@@ -0,0 +1,89 @@
+{-# options_haddock prune #-}
+-- |Description: Entity Effects, Internal
+module Polysemy.Http.Effect.Entity where
+
+import Polysemy (makeSem_)
+
+-- |Generic error type for decoders.
+data EntityError =
+  EntityError {
+    body :: Text,
+    message :: Text
+  }
+  deriving (Eq, Show)
+
+-- |Abstraction of json encoding, potentially usable for other content types like xml.
+data EntityEncode d :: Effect where
+  Encode :: d -> EntityEncode d m LByteString
+  EncodeStrict :: d -> EntityEncode d m ByteString
+
+makeSem_ ''EntityEncode
+
+-- |Lazily encode a value of type @d@ to a 'LByteString'
+encode ::
+  ∀ d r .
+  Member (EntityEncode d) r =>
+  d ->
+  Sem r LByteString
+
+-- |Strictly encode a value of type @d@ to a 'ByteString'
+encodeStrict ::
+  ∀ d r .
+  Member (EntityEncode d) r =>
+  d ->
+  Sem r ByteString
+
+-- |Abstraction of json decoding, potentially usable for other content types like xml.
+data EntityDecode d :: Effect where
+  Decode :: LByteString -> EntityDecode d m (Either EntityError d)
+  DecodeStrict :: ByteString -> EntityDecode d m (Either EntityError d)
+
+makeSem_ ''EntityDecode
+
+-- |Lazily decode a 'LByteString' to a value of type @d@
+decode ::
+  ∀ d r .
+  Member (EntityDecode d) r =>
+  LByteString ->
+  Sem r (Either EntityError d)
+
+-- |Strictly decode a 'ByteString' to a value of type @d@
+decodeStrict ::
+  ∀ d r .
+  Member (EntityDecode d) r =>
+  ByteString ->
+  Sem r (Either EntityError d)
+
+-- |Marker type to be used with 'Entities'
+data Encode a
+
+-- |Marker type to be used with 'Entities'
+data Decode a
+
+-- |Convenience constraint for requiring multiple entity effects, to be used like 'Polysemy.Members'.
+--
+-- @
+-- foo :: Entities [Encode Int, Decode Double] r => Sem r ()
+-- @
+type family Entities es r :: Constraint where
+  Entities '[] r = ()
+  Entities (Encode d ': ds) r = (Member (EntityEncode d) r, Entities ds r)
+  Entities (Decode d ': ds) r = (Member (EntityDecode d) r, Entities ds r)
+
+-- |Convenience constraint for requiring multiple encoders.
+--
+-- @
+-- foo :: Encoders [Int, Double] r => Sem r ()
+-- @
+type family Encoders es r :: Constraint where
+  Encoders '[] r = ()
+  Encoders (d ': ds) r = (Member (EntityEncode d) r, Encoders ds r)
+
+-- |Convenience constraint for requiring multiple decoders.
+--
+-- @
+-- foo :: Decoders [Int, Double] r => Sem r ()
+-- @
+type family Decoders ds r :: Constraint where
+  Decoders '[] r = ()
+  Decoders (d ': ds) r = (Member (EntityDecode d) r, Decoders ds r)
diff --git a/lib/Polysemy/Http/Effect/Http.hs b/lib/Polysemy/Http/Effect/Http.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Effect/Http.hs
@@ -0,0 +1,50 @@
+{-# options_haddock prune #-}
+-- |Description: Http Effect, Internal
+module Polysemy.Http.Effect.Http where
+
+import Polysemy (makeSem_)
+
+import Polysemy.Http.Data.HttpError (HttpError)
+import Polysemy.Http.Data.Request (Request)
+import Polysemy.Http.Data.Response (Response)
+
+-- |The main effect for HTTP requests.
+-- The parameter @c@ determines the representation of raw chunks.
+data Http c :: Effect where
+  Response :: Request -> (Response c -> m a) -> Http c m (Either HttpError a)
+  Request :: Request -> Http c m (Either HttpError (Response LByteString))
+  Stream :: Request -> (Response c -> m a) -> Http c m (Either HttpError a)
+  -- |Internal action for streaming transfers.
+  ConsumeChunk :: c -> Http c m (Either HttpError ByteString)
+
+makeSem_ ''Http
+
+-- |Bracket a higher-order action with a 'Response' that has been opened while its body hasn't been fetched.
+response ::
+  ∀ c r a .
+  Member (Http c) r =>
+  Request ->
+  (Response c -> Sem r a) ->
+  Sem r (Either HttpError a)
+
+-- |Synchronously run an HTTP request and return the response.
+request ::
+  ∀ c r .
+  Member (Http c) r =>
+  Request ->
+  Sem r (Either HttpError (Response LByteString))
+
+-- |Open a connection without consuming data and pass the response to a handler for custom transmission.
+-- The intended purpose is to allow streaming transfers.
+stream ::
+  ∀ c r a .
+  Member (Http c) r =>
+  Request ->
+  (Response c -> Sem r a) ->
+  Sem r (Either HttpError a)
+
+consumeChunk ::
+  ∀ c r .
+  Member (Http c) r =>
+  c ->
+  Sem r (Either HttpError ByteString)
diff --git a/lib/Polysemy/Http/Effect/Manager.hs b/lib/Polysemy/Http/Effect/Manager.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Effect/Manager.hs
@@ -0,0 +1,11 @@
+{-# options_haddock prune #-}
+-- |Description: Manager Effect, Internal
+module Polysemy.Http.Effect.Manager where
+
+import qualified Network.HTTP.Client as HTTP (Manager)
+
+-- |This effect abstracts the creation of a 'Manager' in order to allow pool sharing in a flexible way.
+data Manager :: Effect where
+  Get :: Manager m HTTP.Manager
+
+makeSem ''Manager
diff --git a/lib/Polysemy/Http/Http.hs b/lib/Polysemy/Http/Http.hs
--- a/lib/Polysemy/Http/Http.hs
+++ b/lib/Polysemy/Http/Http.hs
@@ -1,16 +1,18 @@
+{-# options_haddock prune #-}
+-- |Description: Streaming Implementation, Internal
 module Polysemy.Http.Http where
 
 import qualified Data.ByteString as ByteString
+import Polysemy.Resource (Resource, bracket)
 
-import qualified Polysemy.Http.Data.Http as Http
-import Polysemy.Http.Data.Http (Http)
 import Polysemy.Http.Data.HttpError (HttpError)
 import Polysemy.Http.Data.Request (Request)
-import Polysemy.Http.Data.Response (Response(Response))
-import Polysemy.Http.Data.StreamChunk (StreamChunk(StreamChunk))
+import Polysemy.Http.Data.Response (Response (Response))
+import Polysemy.Http.Data.StreamChunk (StreamChunk (StreamChunk))
 import qualified Polysemy.Http.Data.StreamEvent as StreamEvent
 import Polysemy.Http.Data.StreamEvent (StreamEvent)
-import Polysemy.Resource (Resource, bracket)
+import qualified Polysemy.Http.Effect.Http as Http
+import Polysemy.Http.Effect.Http (Http)
 
 streamLoop ::
   Members [Http c, Error HttpError] r =>
diff --git a/lib/Polysemy/Http/Interpreter/AesonEntity.hs b/lib/Polysemy/Http/Interpreter/AesonEntity.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Interpreter/AesonEntity.hs
@@ -0,0 +1,65 @@
+{-# options_haddock prune #-}
+-- |Description: Entity Aeson Interpreters, Internal
+module Polysemy.Http.Interpreter.AesonEntity where
+
+import Data.Aeson (eitherDecode', eitherDecodeStrict', encode)
+
+import Polysemy.Http.Effect.Entity (EntityDecode, EntityEncode, EntityError (EntityError))
+import qualified Polysemy.Http.Effect.Entity as Entity (EntityDecode (..), EntityEncode (..))
+
+-- |Interpreter for 'EntityEncode' that uses Aeson and a different codec type.
+-- The first parameter is the conversion function.
+interpretEntityEncodeAesonAs ::
+  ToJSON j =>
+  (d -> j) ->
+  Sem (EntityEncode d : r) a ->
+  Sem r a
+interpretEntityEncodeAesonAs convert =
+  interpret \case
+    Entity.Encode a ->
+      pure (encode (convert a))
+    Entity.EncodeStrict a ->
+      pure (toStrict (encode (convert a)))
+{-# inline interpretEntityEncodeAesonAs #-}
+
+-- |Interpreter for 'EntityEncode' that uses Aeson.
+interpretEntityEncodeAeson ::
+  ToJSON d =>
+  Sem (EntityEncode d : r) a ->
+  Sem r a
+interpretEntityEncodeAeson =
+  interpretEntityEncodeAesonAs id
+{-# inline interpretEntityEncodeAeson #-}
+
+decodeWith ::
+  ConvertUtf8 Text s =>
+  (s -> Either String a) ->
+  s ->
+  Sem r (Either EntityError a)
+decodeWith dec body =
+  pure . mapLeft (EntityError (decodeUtf8 body) . toText) $ dec body
+{-# inline decodeWith #-}
+
+-- |Interpreter for 'EntityDecode' that uses Aeson and a different codec type.
+-- The first parameter is the conversion function.
+interpretEntityDecodeAesonAs ::
+  FromJSON j =>
+  (j -> d) ->
+  Sem (EntityDecode d : r) a ->
+  Sem r a
+interpretEntityDecodeAesonAs convert =
+  interpret \case
+    Entity.Decode body ->
+      fmap convert <$> decodeWith eitherDecode' body
+    Entity.DecodeStrict body ->
+      fmap convert <$> decodeWith eitherDecodeStrict' body
+{-# inline interpretEntityDecodeAesonAs #-}
+
+-- |Interpreter for 'EntityDecode' that uses Aeson.
+interpretEntityDecodeAeson ::
+  FromJSON d =>
+  Sem (EntityDecode d : r) a ->
+  Sem r a
+interpretEntityDecodeAeson =
+  interpretEntityDecodeAesonAs id
+{-# inline interpretEntityDecodeAeson #-}
diff --git a/lib/Polysemy/Http/Interpreter/Manager.hs b/lib/Polysemy/Http/Interpreter/Manager.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Interpreter/Manager.hs
@@ -0,0 +1,28 @@
+{-# options_haddock prune #-}
+-- |Description: Manager Interpreters, Internal
+module Polysemy.Http.Interpreter.Manager where
+
+import Network.HTTP.Client (newManager)
+import qualified Network.HTTP.Client as HTTP (Manager)
+import Network.HTTP.Client.TLS (mkManagerSettings)
+
+import Polysemy.Http.Effect.Manager (Manager (..))
+
+interpretManagerWith ::
+  HTTP.Manager ->
+  InterpreterFor Manager r
+interpretManagerWith manager = do
+  interpret \ Get -> pure manager
+{-# inline interpretManagerWith #-}
+
+-- |Trivial interpreter with a globally shared 'Manager' instance.
+interpretManager ::
+  Member (Embed IO) r =>
+  InterpreterFor Manager r
+interpretManager sem = do
+  manager <- embed (newManager settings)
+  interpretManagerWith manager sem
+  where
+    settings =
+      mkManagerSettings def Nothing
+{-# inline interpretManager #-}
diff --git a/lib/Polysemy/Http/Interpreter/Native.hs b/lib/Polysemy/Http/Interpreter/Native.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Interpreter/Native.hs
@@ -0,0 +1,145 @@
+{-# options_haddock prune #-}
+-- |Description: Http Interpreters, Internal
+module Polysemy.Http.Interpreter.Native where
+
+import qualified Data.CaseInsensitive as CaseInsensitive
+import Data.CaseInsensitive (foldedCase)
+import qualified Network.HTTP.Client as HTTP
+import Network.HTTP.Client (BodyReader, httpLbs, responseClose, responseOpen)
+import Network.HTTP.Client.Internal (CookieJar (CJ))
+import Polysemy (getInitialStateT, interpretH, runTSimple)
+import qualified Polysemy.Log as Log
+import Polysemy.Log (Log)
+import Polysemy.Resource (Resource, bracket)
+
+import Polysemy.Http.Data.Header (Header (Header), unHeaderName, unHeaderValue)
+import qualified Polysemy.Http.Data.HttpError as HttpError
+import Polysemy.Http.Data.HttpError (HttpError)
+import Polysemy.Http.Data.Request (
+  Body (Body),
+  Host (Host),
+  Path (Path),
+  Request (Request),
+  Tls (Tls),
+  methodUpper,
+  unPort,
+  unQueryKey,
+  unQueryValue,
+  )
+import Polysemy.Http.Data.Response (Response (Response))
+import qualified Polysemy.Http.Effect.Http as Http
+import Polysemy.Http.Effect.Http (Http)
+import qualified Polysemy.Http.Effect.Manager as Manager
+import Polysemy.Http.Effect.Manager (Manager)
+import Polysemy.Http.Interpreter.Manager (interpretManager)
+
+-- |Converts a 'Request' to a native 'N.Request'.
+nativeRequest :: Request -> HTTP.Request
+nativeRequest (Request method (Host host) portOverride (Tls tls) (Path path) headers (CJ cookies) query (Body body)) =
+  HTTP.setQueryString queryParams HTTP.defaultRequest {
+    HTTP.host = encodeUtf8 host,
+    HTTP.port = port,
+    HTTP.secure = tls,
+    HTTP.method = encodeUtf8 (methodUpper method),
+    HTTP.requestHeaders = encodedHeaders,
+    HTTP.path = encodeUtf8 path,
+    HTTP.requestBody = HTTP.RequestBodyLBS body,
+    HTTP.cookieJar = CJ . toList <$> nonEmpty cookies
+  }
+  where
+    queryParams =
+      bimap (encodeUtf8 . unQueryKey) (fmap (encodeUtf8 . unQueryValue)) <$> query
+    port =
+      maybe (if tls then 443 else 80) unPort portOverride
+    encodedHeaders =
+      bimap (CaseInsensitive.mk . encodeUtf8 . unHeaderName) (encodeUtf8 . unHeaderValue) <$> headers
+
+convertResponse :: HTTP.Response b -> Response b
+convertResponse response =
+  Response (HTTP.responseStatus response) (HTTP.responseBody response) headers (HTTP.responseCookieJar response)
+  where
+    headers =
+      header <$> HTTP.responseHeaders response
+    header (foldedCase -> decodeUtf8 -> name, decodeUtf8 -> value) =
+      Header (fromString name) (fromString value)
+
+internalError ::
+  Member (Embed IO) r =>
+  IO a ->
+  Sem r (Either HttpError a)
+internalError =
+  tryHoist HttpError.Internal
+
+executeRequest ::
+  Member (Embed IO) r =>
+  HTTP.Manager ->
+  Request ->
+  Sem r (Either HttpError (Response LByteString))
+executeRequest manager request =
+  fmap convertResponse <$> internalError (httpLbs (nativeRequest request) manager)
+
+withResponse ::
+  Members [Embed IO, Log, Resource, Manager] r =>
+  Request ->
+  (Response BodyReader -> Sem r a) ->
+  Sem r (Either HttpError a)
+withResponse request f =
+  bracket acquire release use
+  where
+    acquire = do
+      manager <- Manager.get
+      internalError (responseOpen (nativeRequest request) manager)
+    release (Right response) =
+      tryAny (responseClose response) >>= traverseLeft closeFailed
+    release (Left _) =
+      unit
+    use (Right response) = do
+      Right <$> f (convertResponse response)
+    use (Left err) =
+      pure (Left err)
+    closeFailed err =
+      Log.error [qt|closing response failed: #{err}|]
+{-# inline withResponse #-}
+
+distribEither ::
+  Functor f =>
+  Either err (f a) ->
+  Sem (WithTactics e f m r) (f (Either err a))
+distribEither = \case
+  Right fa ->
+    pure (Right <$> fa)
+  Left err -> do
+    s <- getInitialStateT
+    pure (Left err <$ s)
+{-# inline distribEither #-}
+
+-- |Same as 'interpretHttpNative', but the interpretation of 'Manager' is left to the user.
+interpretHttpNativeWith ::
+  Members [Embed IO, Log, Resource, Manager] r =>
+  InterpreterFor (Http BodyReader) r
+interpretHttpNativeWith =
+  interpretH \case
+    Http.Response request f -> do
+      distribEither =<< withResponse request ((\x -> runTSimple x) . f)
+    Http.Request request -> do
+      Log.debug [qt|http request: #{request}|]
+      manager <- Manager.get
+      liftT do
+        response <- executeRequest manager request
+        response <$ Log.debug [qt|http response: #{response}|]
+    Http.Stream request handler -> do
+      Log.debug [qt|http stream request: #{request}|]
+      distribEither =<< withResponse request ((\x -> runTSimple x). handler)
+    Http.ConsumeChunk body ->
+      pureT . first HttpError.ChunkFailed =<< tryAny body
+{-# inline interpretHttpNativeWith #-}
+
+-- |Interpret @'Http' 'BodyReader'@ using the native 'Network.HTTP.Client' implementation.
+-- 'BodyReader' is an alias for @'IO' 'ByteString'@, it is how http-client represents chunks.
+-- This uses the default interpreter for 'Manager'.
+interpretHttpNative ::
+  Members [Embed IO, Log, Resource] r =>
+  InterpreterFor (Http BodyReader) r
+interpretHttpNative =
+  interpretManager . interpretHttpNativeWith . raiseUnder
+{-# inline interpretHttpNative #-}
diff --git a/lib/Polysemy/Http/Interpreter/Pure.hs b/lib/Polysemy/Http/Interpreter/Pure.hs
new file mode 100644
--- /dev/null
+++ b/lib/Polysemy/Http/Interpreter/Pure.hs
@@ -0,0 +1,62 @@
+{-# options_haddock prune #-}
+-- |Description: Pure Http Interpreters, Internal
+module Polysemy.Http.Interpreter.Pure where
+
+import Network.HTTP.Client.Internal (CookieJar (CJ))
+import Polysemy (interpretH)
+import Polysemy.Internal.Tactics (bindT, bindTSimple)
+
+import Polysemy.Http.Data.Response (Response (Response))
+import qualified Polysemy.Http.Effect.Http as Http
+import Polysemy.Http.Effect.Http (Http)
+
+takeResponse ::
+  Member (State [Response LByteString]) r =>
+  [Response LByteString] ->
+  Sem r (Response LByteString)
+takeResponse (response : rest) =
+  response <$ put rest
+takeResponse [] =
+  pure (Response (toEnum 502) "test responses exhausted" [] (CJ mempty))
+
+takeChunk ::
+  Member (State [ByteString]) r =>
+  [ByteString] ->
+  Sem r ByteString
+takeChunk (chunk : rest) =
+  chunk <$ put rest
+takeChunk [] =
+  pure ""
+
+interpretHttpPureWithState ::
+  Members [State [ByteString], State [Response LByteString], Embed IO] r =>
+  InterpreterFor (Http LByteString) r
+interpretHttpPureWithState =
+  interpretH \case
+    Http.Response _ f -> do
+      res <- liftT . takeResponse =<< raise get
+      fmap Right <$> bindTSimple f res
+    Http.Request _ ->
+      liftT . fmap Right . takeResponse =<< raise get
+    Http.Stream _ handler -> do
+      handle <- bindT handler
+      res <- liftT . takeResponse =<< raise get
+      fmap Right <$> raise (interpretHttpPureWithState (handle res))
+    Http.ConsumeChunk _ ->
+      liftT . fmap Right . takeChunk =<< raise get
+{-# inline interpretHttpPureWithState #-}
+
+-- |In-Memory interpreter for 'Http'.
+interpretHttpPure ::
+  Member (Embed IO) r =>
+  -- |When a request is made, one response is popped of the list and returned.
+  --   If the list is exhausted, a 502 response is returned.
+  [Response LByteString] ->
+  -- |Chunks used for streaming responses.
+  [ByteString] ->
+  InterpretersFor [Http LByteString, State [Response LByteString], State [ByteString]] r
+interpretHttpPure responses chunks =
+  evalState chunks .
+  evalState responses .
+  interpretHttpPureWithState
+{-# inline interpretHttpPure #-}
diff --git a/lib/Polysemy/Http/Json.hs b/lib/Polysemy/Http/Json.hs
--- a/lib/Polysemy/Http/Json.hs
+++ b/lib/Polysemy/Http/Json.hs
@@ -1,19 +1,22 @@
+{-# options_haddock prune #-}
+-- |Description: Json Request Combinator, Internal
 module Polysemy.Http.Json where
 
 import Control.Lens ((%~))
 
 import Polysemy.Http.Data.Header (HeaderName, HeaderValue)
-import qualified Polysemy.Http.Data.Http as Http
-import Polysemy.Http.Data.Http (Http)
 import Polysemy.Http.Data.HttpError (HttpError)
 import qualified Polysemy.Http.Data.Request as Request
 import Polysemy.Http.Data.Request (Request)
 import Polysemy.Http.Data.Response (Response)
+import qualified Polysemy.Http.Effect.Http as Http
+import Polysemy.Http.Effect.Http (Http)
 
 jsonContentType :: (HeaderName, HeaderValue)
 jsonContentType =
   ("content-type", "application/json")
 
+-- |Make a request, setting the @content-type@ header to @application/json@
 jsonRequest ::
   Member (Http c) r =>
   Request ->
diff --git a/lib/Polysemy/Http/Manager.hs b/lib/Polysemy/Http/Manager.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Manager.hs
+++ /dev/null
@@ -1,26 +0,0 @@
-module Polysemy.Http.Manager where
-
-import Network.HTTP.Client (newManager)
-import qualified Network.HTTP.Client as HTTP (Manager)
-import Network.HTTP.Client.TLS (mkManagerSettings)
-
-import Polysemy.Http.Data.Manager (Manager(..))
-
-interpretManagerWith ::
-  HTTP.Manager ->
-  InterpreterFor Manager r
-interpretManagerWith manager = do
-  interpret \ Get -> pure manager
-{-# INLINE interpretManagerWith #-}
-
--- |Trivial interpreter with a globally shared 'Manager' instance.
-interpretManager ::
-  Member (Embed IO) r =>
-  InterpreterFor Manager r
-interpretManager sem = do
-  manager <- embed (newManager settings)
-  interpretManagerWith manager sem
-  where
-    settings =
-      mkManagerSettings def Nothing
-{-# INLINE interpretManager #-}
diff --git a/lib/Polysemy/Http/Native.hs b/lib/Polysemy/Http/Native.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Native.hs
+++ /dev/null
@@ -1,143 +0,0 @@
-module Polysemy.Http.Native where
-
-import qualified Data.CaseInsensitive as CaseInsensitive
-import Data.CaseInsensitive (foldedCase)
-import qualified Network.HTTP.Client as HTTP
-import Network.HTTP.Client (BodyReader, httpLbs, responseClose, responseOpen)
-import Network.HTTP.Client.Internal (CookieJar(CJ))
-import Polysemy (getInitialStateT, interpretH, runTSimple)
-import qualified Polysemy.Log as Log
-import Polysemy.Log (Log)
-import Polysemy.Resource (Resource, bracket)
-
-import Polysemy.Http.Data.Header (Header(Header), unHeaderName, unHeaderValue)
-import qualified Polysemy.Http.Data.Http as Http
-import Polysemy.Http.Data.Http (Http)
-import qualified Polysemy.Http.Data.HttpError as HttpError
-import Polysemy.Http.Data.HttpError (HttpError)
-import qualified Polysemy.Http.Data.Manager as Manager
-import Polysemy.Http.Data.Manager (Manager)
-import Polysemy.Http.Data.Request (
-  Body(Body),
-  Host(Host),
-  Path(Path),
-  Request(Request),
-  Tls(Tls),
-  methodUpper,
-  unPort,
-  unQueryKey,
-  unQueryValue,
-  )
-import Polysemy.Http.Data.Response (Response(Response))
-import Polysemy.Http.Manager (interpretManager)
-
--- |Converts a 'Request' to a native 'N.Request'.
-nativeRequest :: Request -> HTTP.Request
-nativeRequest (Request method (Host host) portOverride (Tls tls) (Path path) headers (CJ cookies) query (Body body)) =
-  HTTP.setQueryString queryParams HTTP.defaultRequest {
-    HTTP.host = encodeUtf8 host,
-    HTTP.port = port,
-    HTTP.secure = tls,
-    HTTP.method = encodeUtf8 (methodUpper method),
-    HTTP.requestHeaders = encodedHeaders,
-    HTTP.path = encodeUtf8 path,
-    HTTP.requestBody = HTTP.RequestBodyLBS body,
-    HTTP.cookieJar = CJ . toList <$> nonEmpty cookies
-  }
-  where
-    queryParams =
-      bimap (encodeUtf8 . unQueryKey) (fmap (encodeUtf8 . unQueryValue)) <$> query
-    port =
-      maybe (if tls then 443 else 80) unPort portOverride
-    encodedHeaders =
-      bimap (CaseInsensitive.mk . encodeUtf8 . unHeaderName) (encodeUtf8 . unHeaderValue) <$> headers
-
-convertResponse :: HTTP.Response b -> Response b
-convertResponse response =
-  Response (HTTP.responseStatus response) (HTTP.responseBody response) headers (HTTP.responseCookieJar response)
-  where
-    headers =
-      header <$> HTTP.responseHeaders response
-    header (foldedCase -> decodeUtf8 -> name, decodeUtf8 -> value) =
-      Header (fromString name) (fromString value)
-
-internalError ::
-  Member (Embed IO) r =>
-  IO a ->
-  Sem r (Either HttpError a)
-internalError =
-  tryHoist HttpError.Internal
-
-executeRequest ::
-  Member (Embed IO) r =>
-  HTTP.Manager ->
-  Request ->
-  Sem r (Either HttpError (Response LByteString))
-executeRequest manager request =
-  fmap convertResponse <$> internalError (httpLbs (nativeRequest request) manager)
-
-withResponse ::
-  Members [Embed IO, Log, Resource, Manager] r =>
-  Request ->
-  (Response BodyReader -> Sem r a) ->
-  Sem r (Either HttpError a)
-withResponse request f =
-  bracket acquire release use
-  where
-    acquire = do
-      manager <- Manager.get
-      internalError (responseOpen (nativeRequest request) manager)
-    release (Right response) =
-      tryAny (responseClose response) >>= traverseLeft closeFailed
-    release (Left _) =
-      unit
-    use (Right response) = do
-      Right <$> f (convertResponse response)
-    use (Left err) =
-      pure (Left err)
-    closeFailed err =
-      Log.error [qt|closing response failed: #{err}|]
-{-# INLINE withResponse #-}
-
-distribEither ::
-  Functor f =>
-  Either err (f a) ->
-  Sem (WithTactics e f m r) (f (Either err a))
-distribEither = \case
-  Right fa ->
-    pure (Right <$> fa)
-  Left err -> do
-    s <- getInitialStateT
-    pure (Left err <$ s)
-{-# INLINE distribEither #-}
-
--- |Same as 'interpretHttpNative', but the interpretation of 'Manager' is left to the user.
-interpretHttpNativeWith ::
-  Members [Embed IO, Log, Resource, Manager] r =>
-  InterpreterFor (Http BodyReader) r
-interpretHttpNativeWith =
-  interpretH \case
-    Http.Response request f -> do
-      distribEither =<< withResponse request ((\x -> runTSimple x) . f)
-    Http.Request request -> do
-      Log.debug [qt|http request: #{request}|]
-      manager <- Manager.get
-      liftT do
-        response <- executeRequest manager request
-        response <$ Log.debug [qt|http response: #{response}|]
-    Http.Stream request handler -> do
-      Log.debug [qt|http stream request: #{request}|]
-      distribEither =<< withResponse request ((\x -> runTSimple x). handler)
-    Http.ConsumeChunk body ->
-      pureT . first HttpError.ChunkFailed =<< tryAny body
-{-# INLINE interpretHttpNativeWith #-}
-
--- |Interpret @'Http' 'BodyReader'@ using the native 'Network.HTTP.Client' implementation.
--- 'BodyReader' is an alias for @'IO' 'ByteString'@, it is how http-client represents chunks.
--- This uses the default interpreter for 'Manager'.
-interpretHttpNative ::
-  Members [Embed IO, Log, Resource] r =>
-  InterpreterFor (Http BodyReader) r
-interpretHttpNative =
-  interpretManager . interpretHttpNativeWith . raiseUnder
-{-# INLINE interpretHttpNative #-}
diff --git a/lib/Polysemy/Http/Prelude.hs b/lib/Polysemy/Http/Prelude.hs
--- a/lib/Polysemy/Http/Prelude.hs
+++ b/lib/Polysemy/Http/Prelude.hs
@@ -1,3 +1,5 @@
+{-# options_haddock hide #-}
+-- |Description: Prelude, Internal
 {-# LANGUAGE NoImplicitPrelude #-}
 
 module Polysemy.Http.Prelude (
@@ -21,7 +23,7 @@
 import Data.Aeson.TH (deriveFromJSON, deriveJSON)
 import qualified Data.Aeson.TH as Aeson (Options, defaultOptions, unwrapUnaryRecords)
 import Data.Composition ((.:))
-import Data.Default (Default(def))
+import Data.Default (Default (def))
 import Data.Either.Combinators (mapLeft)
 import Data.Foldable (foldl, traverse_)
 import Data.Map.Strict (Map)
@@ -34,6 +36,7 @@
   Effect,
   Embed,
   InterpreterFor,
+  InterpretersFor,
   Member,
   Members,
   Sem,
@@ -72,24 +75,24 @@
 dbg msg = do
   () <- return $ unsafePerformIO (putStrLn (toString msg))
   return ()
-{-# INLINE dbg #-}
+{-# inline dbg #-}
 
 dbgs :: Monad m => Show a => a -> m ()
 dbgs a =
   dbg (show a)
-{-# INLINE dbgs_ #-}
+{-# inline dbgs_ #-}
 
 dbgs_ :: Monad m => Show a => a -> m a
 dbgs_ a =
   a <$ dbg (show a)
-{-# INLINE dbgs #-}
+{-# inline dbgs #-}
 
 unit ::
   Applicative f =>
   f ()
 unit =
   pure ()
-{-# INLINE unit #-}
+{-# inline unit #-}
 
 tuple ::
   Applicative f =>
@@ -98,27 +101,27 @@
   f (a, b)
 tuple fa fb =
   (,) <$> fa <*> fb
-{-# INLINE tuple #-}
+{-# inline tuple #-}
 
 unsafeLogSAnd :: Show a => a -> b -> b
 unsafeLogSAnd a b =
   unsafePerformIO $ print a >> return b
-{-# INLINE unsafeLogSAnd #-}
+{-# inline unsafeLogSAnd #-}
 
 unsafeLogAnd :: Text -> b -> b
 unsafeLogAnd a b =
   unsafePerformIO $ putStrLn (toString a) >> return b
-{-# INLINE unsafeLogAnd #-}
+{-# inline unsafeLogAnd #-}
 
 unsafeLogS :: Show a => a -> a
 unsafeLogS a =
   unsafePerformIO $ print a >> return a
-{-# INLINE unsafeLogS #-}
+{-# inline unsafeLogS #-}
 
 qt :: QuasiQuoter
 qt =
   i
-{-# INLINE qt #-}
+{-# inline qt #-}
 
 liftT ::
   forall m f r e a .
@@ -127,7 +130,7 @@
   Sem (WithTactics e f m r) (f a)
 liftT =
   pureT <=< raise
-{-# INLINE liftT #-}
+{-# inline liftT #-}
 
 defaultOptions :: Aeson.Options
 defaultOptions =
@@ -171,7 +174,7 @@
   m b
 traverseLeft f =
   either f pure
-{-# INLINE traverseLeft #-}
+{-# inline traverseLeft #-}
 
 defaultJson :: TH.Name -> TH.Q [TH.Dec]
 defaultJson =
diff --git a/lib/Polysemy/Http/Request.hs b/lib/Polysemy/Http/Request.hs
--- a/lib/Polysemy/Http/Request.hs
+++ b/lib/Polysemy/Http/Request.hs
@@ -1,16 +1,18 @@
+{-# options_haddock prune #-}
+-- |Description: Request Combinators, Internal
 module Polysemy.Http.Request where
 
 import Control.Lens ((%~))
 import qualified Data.Text as Text
-import Data.Time (UTCTime(UTCTime))
-import Network.HTTP.Client (Cookie(Cookie))
-import Network.HTTP.Client.Internal (CookieJar(CJ, expose))
-import Prelude hiding (get, put)
-
+import Data.Time (UTCTime (UTCTime))
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
+import Network.HTTP.Client (Cookie (Cookie))
+import Network.HTTP.Client.Internal (CookieJar (CJ, expose))
+import Prelude hiding (get, put)
+
 import qualified Polysemy.Http.Data.Request as Request
-import Polysemy.Http.Data.Request (Body, Host(Host), Method(..), Path(Path), Port(Port), Request(Request), Tls(Tls))
+import Polysemy.Http.Data.Request (Body, Host (Host), Method (..), Path (Path), Port (Port), Request (Request), Tls (Tls))
 
 invalidScheme ::
   Text ->
diff --git a/lib/Polysemy/Http/Strict.hs b/lib/Polysemy/Http/Strict.hs
deleted file mode 100644
--- a/lib/Polysemy/Http/Strict.hs
+++ /dev/null
@@ -1,70 +0,0 @@
-module Polysemy.Http.Strict where
-
-import Polysemy (interpretH)
-import Polysemy.Internal.Tactics (bindT, bindTSimple)
-
-import Polysemy.Http.Data.Header (Header(Header))
-import qualified Polysemy.Http.Data.Http as Http
-import Polysemy.Http.Data.Http (Http)
-import Polysemy.Http.Data.Response (Response(Response))
-import Network.HTTP.Client.Internal (CookieJar(CJ))
-
-takeResponse ::
-  Member (State [Response LByteString]) r =>
-  [Response LByteString] ->
-  Sem r (Response LByteString)
-takeResponse (response : rest) =
-  response <$ put rest
-takeResponse [] =
-  pure (Response (toEnum 502) "test responses exhausted" [] (CJ mempty))
-
-takeChunk ::
-  Member (State [ByteString]) r =>
-  [ByteString] ->
-  Sem r ByteString
-takeChunk (chunk : rest) =
-  chunk <$ put rest
-takeChunk [] =
-  pure ""
-
-streamResponse :: Response LByteString
-streamResponse =
-  Response (toEnum 200) "stream response" [
-    Header "content-disposition" [qt|filename="file.txt"|],
-    Header "content-length" "5000000"
-    ] (CJ mempty)
-
-interpretHttpStrictWithState ::
-  Members [State [ByteString], State [Response LByteString], Embed IO] r =>
-  InterpreterFor (Http LByteString) r
-interpretHttpStrictWithState =
-  interpretH \case
-    Http.Response _ f -> do
-      res <- liftT . takeResponse =<< raise get
-      fmap Right <$> bindTSimple f res
-    Http.Request _ ->
-      liftT . fmap Right . takeResponse =<< raise get
-    Http.Stream _ handler -> do
-      handle <- bindT handler
-      resp <- pureT streamResponse
-      fmap Right <$> raise (interpretHttpStrictWithState (handle resp))
-    Http.ConsumeChunk _ ->
-      liftT . fmap Right . takeChunk =<< raise get
-{-# INLINE interpretHttpStrictWithState #-}
-
--- |In-Memory interpreter for 'Http'.
-interpretHttpStrict ::
-  Member (Embed IO) r =>
-  -- |When a request is made, one response is popped of the head and returned.
-  --   If the list is exhausted, a 502 response is returned.
-  [Response LByteString] ->
-  -- |Chunks used for streaming responses.
-  [ByteString] ->
-  InterpreterFor (Http LByteString) r
-interpretHttpStrict responses chunks =
-  evalState chunks .
-  evalState responses .
-  interpretHttpStrictWithState .
-  raiseUnder .
-  raiseUnder
-{-# INLINE interpretHttpStrict #-}
diff --git a/lib/Prelude.hs b/lib/Prelude.hs
--- a/lib/Prelude.hs
+++ b/lib/Prelude.hs
@@ -1,3 +1,5 @@
+{-# options_haddock hide #-}
+-- |Description: Prelude, Internal
 module Prelude (
   module Polysemy.Http.Prelude,
 ) where
diff --git a/polysemy-http.cabal b/polysemy-http.cabal
--- a/polysemy-http.cabal
+++ b/polysemy-http.cabal
@@ -5,14 +5,14 @@
 -- see: https://github.com/sol/hpack
 
 name:           polysemy-http
-version:        0.4.0.6
-synopsis:       Polysemy Effect for Http-Client
+version:        0.5.0.0
+synopsis:       Polysemy Effects for HTTP clients
 description:    See <https://hackage.haskell.org/package/polysemy-http/docs/Polysemy-Http.html>
 category:       Network
 homepage:       https://github.com/tek/polysemy-http#readme
 bug-reports:    https://github.com/tek/polysemy-http/issues
 author:         Torsten Schmits
-maintainer:     tek@tryp.io
+maintainer:     haskell@tryp.io
 copyright:      2020 Torsten Schmits
 license:        BSD-2-Clause-Patent
 license-file:   LICENSE
@@ -28,23 +28,23 @@
 library
   exposed-modules:
       Polysemy.Http
-      Polysemy.Http.AesonEntity
-      Polysemy.Http.Data.Entity
       Polysemy.Http.Data.Header
-      Polysemy.Http.Data.Http
       Polysemy.Http.Data.HttpError
-      Polysemy.Http.Data.Manager
       Polysemy.Http.Data.Request
       Polysemy.Http.Data.Response
       Polysemy.Http.Data.StreamChunk
       Polysemy.Http.Data.StreamEvent
+      Polysemy.Http.Effect.Entity
+      Polysemy.Http.Effect.Http
+      Polysemy.Http.Effect.Manager
       Polysemy.Http.Http
+      Polysemy.Http.Interpreter.AesonEntity
+      Polysemy.Http.Interpreter.Manager
+      Polysemy.Http.Interpreter.Native
+      Polysemy.Http.Interpreter.Pure
       Polysemy.Http.Json
-      Polysemy.Http.Manager
-      Polysemy.Http.Native
       Polysemy.Http.Prelude
       Polysemy.Http.Request
-      Polysemy.Http.Strict
   other-modules:
       Prelude
       Paths_polysemy_http
@@ -119,9 +119,9 @@
     , http-client-tls >=0.3.5
     , http-types >=0.12.3
     , lens >=4
-    , polysemy >=1.5
-    , polysemy-log >=0.2.2.1
-    , polysemy-plugin >=0.3
+    , polysemy >=1.6
+    , polysemy-log >=0.2.2.4
+    , polysemy-plugin >=0.4
     , relude >=0.7
     , string-interpolate >=0.2.1
     , template-haskell
@@ -212,10 +212,10 @@
     , http-types >=0.12.3
     , lens >=4
     , network
-    , polysemy >=1.5
+    , polysemy >=1.6
     , polysemy-http
-    , polysemy-log >=0.2.2.1
-    , polysemy-plugin >=0.3
+    , polysemy-log >=0.2.2.4
+    , polysemy-plugin >=0.4
     , relude >=0.7
     , servant
     , servant-client
@@ -311,10 +311,10 @@
     , http-client-tls >=0.3.5
     , http-types >=0.12.3
     , lens >=4
-    , polysemy >=1.5
+    , polysemy >=1.6
     , polysemy-http
-    , polysemy-log >=0.2.2.1
-    , polysemy-plugin >=0.3
+    , polysemy-log >=0.2.2.4
+    , polysemy-plugin >=0.4
     , relude >=0.7
     , string-interpolate >=0.2.1
     , tasty
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -152,7 +152,7 @@
 # Testing
 
 Polysemy makes it very easy to switch the native interpreter for a mock, and
-there is a convenience interpreter named `interpretHttpStrict` that allows you
+there is a convenience interpreter named `interpretHttpPure` that allows you
 to specify a list of responses and chunks that should be produced:
 
 ```haskell
@@ -161,7 +161,7 @@
   result <- runM $
     resourceToIO $
     interpretLogStdout $
-    interpretHttpStrict [Response (toEnum 200) "foo" []] [] $
+    interpretHttpPure [Response (toEnum 200) "foo" []] [] $
     Http.request (Http.get "hackage.haskell.org" "package/polysemy-http")
   print result
 ```
