diff --git a/planb-token-introspection.cabal b/planb-token-introspection.cabal
--- a/planb-token-introspection.cabal
+++ b/planb-token-introspection.cabal
@@ -1,5 +1,5 @@
 name:                planb-token-introspection
-version:             0.1.1.0
+version:             0.1.2.0
 synopsis:            Token Introspection for PlanB
 description:         This package provides token introspection functionality
                      for the PlanB token provider.
diff --git a/src/Network/PlanB/Introspection.hs b/src/Network/PlanB/Introspection.hs
--- a/src/Network/PlanB/Introspection.hs
+++ b/src/Network/PlanB/Introspection.hs
@@ -2,6 +2,7 @@
   ( TokenInfo(..)
   , Conf
   , IntrospectionException(..)
+  , ErrorResponse(..)
   , new
   , newWithManager
   , newFromEnv
diff --git a/src/Network/PlanB/Introspection/Internal.hs b/src/Network/PlanB/Introspection/Internal.hs
--- a/src/Network/PlanB/Introspection/Internal.hs
+++ b/src/Network/PlanB/Introspection/Internal.hs
@@ -6,6 +6,7 @@
   ( TokenInfo(..)
   , Conf
   , IntrospectionException(..)
+  , ErrorResponse(..)
   , new
   , newWithManager
   , newFromEnv
@@ -36,17 +37,15 @@
 new :: (MonadThrow m, MonadIO m)
     => Text
     -> m (TokenIntrospector m)
-new = newWithManager Nothing
+new = newWithBackend (backendIO Nothing)
 
 -- | Create a new PlanB introspector using the provided endpoint and
 -- manager.
 newWithManager :: (MonadThrow m, MonadIO m)
-               => Maybe Manager
+               => Manager
                -> Text
                -> m (TokenIntrospector m)
-newWithManager maybeManager endpoint = do
-  conf <- newConf (backendIO maybeManager) endpoint
-  pure $ TokenIntrospector { introspectToken = introspectTokenImpl conf }
+newWithManager manager = newWithBackend (backendIO (Just manager))
 
 backendIO :: MonadIO m
           => Maybe Manager
@@ -83,7 +82,7 @@
       BackendEnv { .. } = backendEnv backend
   endpoint <- envLookup "PLANB_INTROSPECTION_ENDPOINT" >>= \ case
     Just ep -> pure ep
-    Nothing -> throwM IntrospectionEndpointMissing
+    Nothing -> throwM NoEndpoint
   newWithBackend backend endpoint
 
 -- | Create a new PlanB introspector using the provided backend and endpoint.
@@ -128,10 +127,7 @@
       request     = endpoint { method         = "GET"
                              , path           = "/oauth2/tokeninfo"
                              , requestHeaders = [("Authorization", bearerToken)] }
-      httpBackend = conf
-                    & confBackend
-                    & backendHttp
-  response <- httpRequestExecute httpBackend request
+  response <- httpRequestExecute request
   let body = responseBody response & ByteString.Lazy.toStrict
 
   when (statusCode (responseStatus response) /= 200) $
@@ -141,14 +137,20 @@
     Right tokenInfo ->
       pure tokenInfo
     Left errMsg ->
-      throwM $ IntrospectionDeserialization (Text.pack errMsg) body
+      throwM $ DeserializationFailure (Text.pack errMsg) body
 
+  where backend = conf & confBackend
+        BackendHttp { .. } = backend & backendHttp
+
 bodyToPlanBException
   :: ByteString -> IntrospectionException
 bodyToPlanBException bytes =
   case eitherDecodeStrict bytes of
-    Right err ->
-      IntrospectionError err
+    Right err @ ErrorResponse { .. } ->
+      case errorResponseError of
+        "invalid_token"   -> InvalidToken err
+        "invalid_request" -> InvalidRequest err
+        _                 -> Other err
     Left errMsgStr  ->
       let errMsg = Text.pack errMsgStr
-      in IntrospectionDeserialization errMsg bytes
+      in DeserializationFailure errMsg bytes
diff --git a/src/Network/PlanB/Introspection/Internal/Types.hs b/src/Network/PlanB/Introspection/Internal/Types.hs
--- a/src/Network/PlanB/Introspection/Internal/Types.hs
+++ b/src/Network/PlanB/Introspection/Internal/Types.hs
@@ -49,19 +49,18 @@
   { confIntrospectionRequest :: Request
   , confBackend              :: Backend m }
 
--- | Type for RFC7807 @Problem@ objects.
-data PlanBError = PlanBError
-  { oauth2Error            :: Text
-  , oauth2ErrorDescription :: Maybe Text
-  , oauth2ErrorURI         :: Maybe Text
-  , oauth2ErrorState       :: Maybe Text
+data ErrorResponse = ErrorResponse
+  { errorResponseError            :: Text
+  , errorResponseErrorDescription :: Maybe Text
   } deriving (Show, Eq, Generic)
 
-$(deriveJSON (aesonDrop (length ("oauth2" :: String)) snakeCase) ''PlanBError)
+$(deriveJSON (aesonDrop (length ("errorResponse" :: String)) snakeCase) ''ErrorResponse)
 
-data IntrospectionException = IntrospectionDeserialization Text ByteString
-                            | IntrospectionError PlanBError
-                            | IntrospectionEndpointMissing
+data IntrospectionException = DeserializationFailure Text ByteString
+                            | InvalidRequest ErrorResponse
+                            | InvalidToken ErrorResponse
+                            | Other ErrorResponse
+                            | NoEndpoint
   deriving (Typeable, Show)
 
 instance Exception IntrospectionException
diff --git a/tests/Network/PlanB/Introspection/Internal/Test.hs b/tests/Network/PlanB/Introspection/Internal/Test.hs
--- a/tests/Network/PlanB/Introspection/Internal/Test.hs
+++ b/tests/Network/PlanB/Introspection/Internal/Test.hs
@@ -48,7 +48,7 @@
                   , _testStateEnvironment = Map.empty
                   }
       tokenName = "some-token-name"
-  Left (PlanB.IntrospectionDeserialization _ _) <- try $ runTestStack testState $ do
+  Left (PlanB.DeserializationFailure _ _) <- try $ runTestStack testState $ do
     introspector <- makeTestIntrospector
     void $ introspectToken introspector tokenName
   pure ()
