diff --git a/changes.md b/changes.md
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,31 @@
+0.1.0.11
+
+    * API breaking change: RPC functions no longer take / return instances of Serialize,
+    but they take / return Message values instead.  The reason for this change is that
+    while courier does use Serialize internally to simplify a variety of operations,
+    putting instances of Serialize in RPC functions was misleading: applications would
+    still need to take care to ensure that different RPC requests / responses could
+    be differentiated, otherwise courier might accidentally deserialize a message
+    to the wrong type at an application's request (e.g., inside a hear callback). This
+    can lead to decoding errors, which may actually causes exceptions to be thrown
+    in pure methods.  By leaving the interface to use Message values, its not only
+    consistent with the rest of Endpoint behavior, but it clarifies the responsibility
+    of the calling application to manage their Message payloads on their own.
+    
+    * Improvements to RPC. Previously, it was not possible to differentiate correctly
+    between requests and responses in the same mailbox: the only check was that
+    deserialization succeeded, which wasn't a sufficient test, and it would be possible
+    to deserialize a response as a request, etc.  This lead to spurious errors.
+    
+    * Added functions for detecting presence of messages in an an endpoint's mailbox
+    (while still leaving them unconsumed): mostly added this for applications that
+    need to react to the presence of a particular message, but not actual consume
+    such messages.
+
+    * Exposed the Request / Response types used by RPC.  Applications may benefit
+    from detecting them or otherwise manipulating them outside of normal RPC
+    flow, they are exposed here for that purpose.
+
 0.1.0.10
 
     * Added hear primitive as one-shot counterpart to call.  While handle
diff --git a/courier.cabal b/courier.cabal
--- a/courier.cabal
+++ b/courier.cabal
@@ -1,5 +1,5 @@
 name:                courier
-version:             0.1.0.10
+version:             0.1.0.11
 synopsis:           A message-passing library for simplifying network applications
 description:         Inspired by Erlang's simple message-passing facilities, courier provides roughly similar 
                      capabilities. Applications simply create one or more endpoints, 
@@ -11,7 +11,7 @@
                      (especially for distributed applications) depend upon a message-passing foundation,
                      but the algorithms are sufficiently complex that the details of how those messages
                      are transmitted among nodes are best hidden away and solved separately from
-                     the distributed algorithm itself.  With this in mind, this pacakge aims
+                     the distributed algorithm itself.  With this in mind, this package aims
                      to provide a variety of transports as well as support for common communication
                      idioms (e.g., in order message delivery, selective out of order message delivery,
                      synchronous RPC, etc.). 
diff --git a/src/Network/Endpoints.hs b/src/Network/Endpoints.hs
--- a/src/Network/Endpoints.hs
+++ b/src/Network/Endpoints.hs
@@ -44,6 +44,8 @@
   -- * Selective message reception
   selectMessage,
   selectMessageTimeout,
+  detectMessage,
+  detectMessageTimeout,
   dispatchMessage,
   dispatchMessageTimeout,
   
@@ -272,6 +274,30 @@
 selectMessageTimeout :: Endpoint -> Int -> (Message -> Maybe v) -> IO (Maybe v)
 selectMessageTimeout endpoint delay testFn = do
   resultOrTimeout <- race (selectMessage endpoint testFn) (threadDelay delay)
+  case resultOrTimeout of
+    Left result -> return $ Just result
+    Right () -> return Nothing
+
+{-|
+Find a 'Message' in the 'Endpoint' 'Mailbox' matching the supplied
+test function, or block until one is available.  Note that any such message
+is left in the mailbox, and thus repeated calls to this function could find the
+message if it is not consumed immediately.
+-}
+detectMessage :: Endpoint -> (Message -> Maybe v) -> IO v
+detectMessage endpoint testFn = do
+    msg <- atomically $ findMailbox (endpointMailbox endpoint) testFn
+    return msg
+
+{-|
+Find a 'Message' in the 'Endpoint' 'Mailbox' matching the supplied
+test function, or block until either one is available or the timeout expires.
+Note that any such message is left in the mailbox, and thus repeated calls
+to this function could find the message if it is not consumed immediately.
+-}
+detectMessageTimeout :: Endpoint -> Int -> (Message -> Maybe v) -> IO (Maybe v)
+detectMessageTimeout endpoint delay testFn = do
+  resultOrTimeout <- race (detectMessage endpoint testFn) (threadDelay delay)
   case resultOrTimeout of
     Left result -> return $ Just result
     Right () -> return Nothing
diff --git a/src/Network/RPC.hs b/src/Network/RPC.hs
--- a/src/Network/RPC.hs
+++ b/src/Network/RPC.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE DeriveGeneric #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Network.RPC
@@ -48,8 +49,11 @@
 
     HandleSite,
     handle,
-    hangup
+    hangup,
 
+    Request(..),
+    Response(..)
+
 ) where
 
 -- local imports
@@ -70,46 +74,56 @@
 import Data.UUID.V4
 import Data.Word
 
+import GHC.Generics hiding (from)
+
 --------------------------------------------------------------------------------
 --------------------------------------------------------------------------------
 
 type Method = String
 
+data RPCMessageType = Req | Rsp deriving (Eq,Show,Enum,Generic)
+
+instance Serialize RPCMessageType
+
 type RequestId = (Word32, Word32, Word32, Word32)
 
-data Request a = (Serialize a) => Request {
+data Request = Request {
     requestId :: RequestId,
     requestCaller :: Name,
     requestMethod :: Method,
-    requestArgs :: a
+    requestArgs :: Message
 }
 
-instance (Serialize a) => Serialize (Request a) where 
+instance Serialize Request where
     put req = do
+        put Req
         put $ requestId req
         put $ requestCaller req
         put $ requestMethod req
         put $ requestArgs req
     get = do
+        Req <- get
         rid <- get
         caller <- get
         method <- get
         args <- get
         return $ Request rid caller method args
 
-data Response b = (Serialize b) => Response {
+data Response = Response {
     responseId :: RequestId,
     responseFrom :: Name,
-    responseValue :: b
+    responseValue :: Message
 }
 
-instance (Serialize b) => Serialize (Response b) where
+instance Serialize Response where
     put rsp = do
+        put Rsp
         put $ responseId rsp
         put $ responseFrom rsp
         put $ responseValue rsp
 
     get = do
+        Rsp <- get
         rid <- get
         from <- get
         val <- get
@@ -130,10 +144,10 @@
 
 {-|
 Call a method with the provided arguments on the recipient with the given name.
-A request will be made through the 'CallSite''s 'Endpoint', and then
+
 the caller will wait until a matching response is received.
 -}
-call :: (Serialize a, Serialize b) => CallSite -> Name -> Method -> a -> IO  b
+call :: CallSite -> Name -> Method -> Message -> IO Message
 call (CallSite endpoint from) name method args = do
     ruuid <- nextRandom
     let req = Request {requestId = toWords ruuid,requestCaller = from,requestMethod = method, requestArgs = args}
@@ -154,7 +168,7 @@
 return the value wrapped in 'Just'; otherwise, if the timeout expires
 before the call returns, then return 'Nothing.
 -}
-callWithTimeout :: (Serialize a, Serialize b) => CallSite -> Name -> Method -> Int-> a -> IO  (Maybe b)
+callWithTimeout :: CallSite -> Name -> Method -> Int-> Message -> IO  (Maybe Message)
 callWithTimeout site name method delay args = do
     resultOrTimeout <- race callIt (threadDelay delay)
     case resultOrTimeout of
@@ -168,7 +182,7 @@
 A request will be made through the 'CallSite''s 'Endpoint', and then
 the caller will wait until all matching responses are received.
 -}
-gcall :: (Serialize a, Serialize b) => CallSite -> [Name] -> Method -> a -> IO (M.Map Name b)
+gcall :: CallSite -> [Name] -> Method -> Message -> IO (M.Map Name Message)
 gcall (CallSite endpoint from) names method args = do
     ruuid <- nextRandom
     let req = Request {requestId = toWords ruuid,requestCaller = from,requestMethod = method, requestArgs = args}
@@ -203,7 +217,7 @@
 of that key will be @Nothing@ if no response was received before the timeout, or @Just value@
 if a response was received.
 -}
-gcallWithTimeout :: (Serialize a, Serialize b) => CallSite -> [Name] -> Method -> Int -> a -> IO (M.Map Name (Maybe b))
+gcallWithTimeout :: CallSite -> [Name] -> Method -> Int -> Message -> IO (M.Map Name (Maybe Message))
 gcallWithTimeout (CallSite endpoint from) names method delay args = do
     ruuid <- nextRandom
     let req = Request {requestId = toWords ruuid,requestCaller = from,requestMethod = method, requestArgs = args}
@@ -225,7 +239,7 @@
                         if (rid == (requestId req)) && (elem name names)
                             then Just (name,value)
                             else Nothing
-        recvAll :: (Serialize b) => Request a -> TVar (M.Map Name b) -> IO (M.Map Name b)
+        recvAll :: Request -> TVar (M.Map Name Message) -> IO (M.Map Name Message)
         recvAll req allResults = do
             (replier,result) <- recv req
             newResults <- atomically $ do
@@ -259,7 +273,7 @@
 helps the original requestor of the RPC differentiate responses when the RPC was a group
 call.
 -}
-hear :: (Serialize a,Serialize b) => Endpoint -> Name -> Method -> IO (a,Reply b)
+hear :: Endpoint -> Name -> Method -> IO (Message,Reply Message)
 hear endpoint name method = do
     (caller,rid,args) <- selectMessage endpoint $ \msg -> do
                 case decode msg of
@@ -279,7 +293,7 @@
 timeout (measured in microseconds), or return a 'Just' instance containing both the
 method arguments and a 'Reply' function useful for sending the reply.
 -}
-hearTimeout :: (Serialize a,Serialize b) => Endpoint -> Name -> Method -> Int -> IO (Maybe (a,Reply b))
+hearTimeout :: Endpoint -> Name -> Method -> Int -> IO (Maybe (Message,Reply Message))
 hearTimeout endpoint name method timeout = do
     req <- selectMessageTimeout endpoint timeout $ \msg -> do
                 case decode msg of
@@ -305,7 +319,7 @@
 Handle all RPCs to invoke the indicated 'Method' on the specified 'Endpoint',
 until 'hangup' is called on the returned 'HandleSite'.
 -}
-handle :: (Serialize a, Serialize b) => Endpoint -> Name -> Method -> (a -> IO b) -> IO HandleSite
+handle :: Endpoint -> Name -> Method -> (Message -> IO Message) -> IO HandleSite
 handle endpoint name method fn = do
     task <- async $ handleCall
     return $ HandleSite name task
diff --git a/src/Network/Transport.hs b/src/Network/Transport.hs
--- a/src/Network/Transport.hs
+++ b/src/Network/Transport.hs
@@ -14,6 +14,18 @@
 -- that specific 'Transport' implementations should provide in order to deliver messages
 -- for 'Endpoint's.
 --
+-- The definition of a transport is deliberately low-level in nature.  Unless a specific
+-- transport describes itself as supporting features like guaranteed delivery, applications
+-- should NOT assume that message delivery is reliable.
+--
+-- For example, if a sender sends a message to a name that has not yet been bound, then 
+-- immediately waits on the response for that message, then the application may hang, 
+-- as the original message may have been dropped.
+--
+-- However, many application may find it easier to push features such as reliable
+-- message delivery into a custom transport, leaving the application only having
+-- to concern itself with the messages being delivered rather than how they arrive.
+--
 -----------------------------------------------------------------------------
 
 module Network.Transport (
