diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,17 @@
 # Revision history for pushbullet-types
 
+## 0.4.0.0  -- 2017-08-01
+
+* Make `body` field of link pushes optional. Many channel pushes do not include
+  a body, but only the URL and the title.
+* Improve `Push` parsing. Push sender and receiver information are factored out
+  into separate datatypes, and parsed as a whole. Either you have a full sender
+  (either a client or a channel) or you don't have any of its fields.  Same
+  applies to receivers, except that only clients can be receivers, and don't
+  have names. This fixes a bug where pushes sent by channels could not be
+  parsed, since such pushes omit certain fields, such as `sender_iden` and
+  `sender_email`.
+
 ## 0.3.0.0  -- 2017-07-29
 
 * `PushEphemeral` now supports the `targets` field, which allows to more
diff --git a/pushbullet-types.cabal b/pushbullet-types.cabal
--- a/pushbullet-types.cabal
+++ b/pushbullet-types.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                pushbullet-types
-version:             0.3.0.0
+version:             0.4.0.0
 synopsis:            Datatypes used by the Pushbullet APIs
 -- description:         
 license:             MIT
@@ -21,8 +21,8 @@
 
 source-repository this
   type: git
-  location: https://github.com/tsani/pushbullet-types/releases/tag/v0.3.0.0
-  tag: v0.3.0.0
+  location: https://github.com/tsani/pushbullet-types/releases/tag/v0.4.0.0
+  tag: v0.4.0.0
 
 library
   default-language:    Haskell2010
diff --git a/src/Network/Pushbullet/Types/Push.hs b/src/Network/Pushbullet/Types/Push.hs
--- a/src/Network/Pushbullet/Types/Push.hs
+++ b/src/Network/Pushbullet/Types/Push.hs
@@ -4,9 +4,9 @@
 ( Push(..)
 , PushId(..)
 , PushDirection(..)
+, PushSender(..)
 , PushTarget(..)
 , PushData(..)
-, PushOrigin(..)
 , ExistingPushes(..)
 , simpleNewPush
 ) where
@@ -37,16 +37,32 @@
     , pushModified :: !(EqT 'Existing s PushbulletTime)
     , pushDismissed :: !(EqT 'Existing s Bool)
     , pushDirection :: !(EqT 'Existing s PushDirection)
-    , pushSender :: !(EqT 'Existing s UserId)
-    , pushSenderEmail :: !(EqT 'Existing s EmailAddress)
-    , pushSenderEmailNormalized :: !(EqT 'Existing s EmailAddress)
-    , pushSenderName :: !(EqT 'Existing s Name)
-    , pushReceiver :: !(EqT 'Existing s UserId)
-    , pushReceiverEmail :: !(EqT 'Existing s EmailAddress)
-    , pushReceiverEmailNormalized :: !(EqT 'Existing s EmailAddress)
-    , pushOrigin :: !(EqT 'Existing s (Maybe PushOrigin))
+    , pushSender :: !(EqT 'Existing s PushSender)
+    , pushReceiver :: !(EqT 'Existing s (Maybe PushReceiver))
     }
 
+data PushReceiver
+  = ReceivedByUser
+    { pushReceiverUserId :: !UserId
+    , pushReceiverEmail :: !EmailAddress
+    , pushReceiverEmailNormalized :: !EmailAddress
+    }
+  deriving (Eq, Show)
+
+data PushSender
+  = SentByUser
+    { pushSenderUserId :: !UserId
+    , pushSenderClientId :: !ClientId
+    , pushSenderUserEmail :: !EmailAddress
+    , pushSenderUserEmailNormalized :: !EmailAddress
+    , pushSenderName :: !Name
+    }
+  | SentByChannel
+    { pushSenderChannelId :: !ChannelId
+    , pushSenderName :: !Name
+    }
+  deriving (Eq, Show)
+
 -- | Unique identifier for a push.
 newtype PushId = PushId Text
   deriving (Eq, FromJSON, Show, ToJSON)
@@ -76,7 +92,7 @@
     }
   | LinkPush
     { pushTitle :: !(Maybe Text)
-    , pushBody :: !Text
+    , pushLinkBody :: !(Maybe Text)
     , pushUrl :: !Url
     }
   | FilePush
@@ -90,12 +106,6 @@
     , pushImageHeight :: !(EqT 'Existing s (Maybe Int))
     }
 
--- | The origin of a push.
-data PushOrigin
-  = FromClient !ClientId
-  | FromChannel !ChannelId
-  deriving (Eq, Show)
-
 -- | A newtype wrapper for a list of existing pushes. We need this to get a
 -- nonstandard 'FromJSON' instance for the list, because Pushbullet gives us
 -- the list wrapped in a trivial object with one key.
@@ -119,13 +129,7 @@
   , pushDismissed = ()
   , pushDirection = ()
   , pushSender = ()
-  , pushSenderEmail = ()
-  , pushSenderEmailNormalized = ()
-  , pushSenderName = ()
   , pushReceiver = ()
-  , pushReceiverEmail = ()
-  , pushReceiverEmailNormalized = ()
-  , pushOrigin = ()
   }
 
 instance ToJSON PushDirection where
@@ -157,6 +161,7 @@
 instance FromJSON (Push 'Existing) where
   parseJSON (Object o) = do
     pushType <- o .: "type"
+
     d <- case id @Text pushType of
       "note" -> pure NotePush
         <*> o .:? "title"
@@ -172,13 +177,27 @@
         <*> o .:? "image_height"
       "link" -> pure LinkPush
         <*> o .:? "title"
-        <*> o .: "body"
+        <*> o .:? "body"
         <*> o .: "url"
       _ -> fail "unrecognized push type"
+
     client <- o .:? "client_iden"
     channel <- o .:? "channel_iden"
-    let origin = (FromClient <$> client) <|> (FromChannel <$> channel)
+    email <- o .:? "sender_email"
+    emailNorm <- o .:? "sender_email_normalized"
+    user <- o .:? "sender_iden"
+    name <- o .:? "sender_name"
 
+    let u = SentByUser <$> user <*> client <*> email <*> emailNorm <*> name
+    let c = SentByChannel <$> channel <*> name
+    sender <- maybe (fail "push not sent by channel or by user") pure (u <|> c)
+
+    recvId <- o .:? "receiver_iden"
+    recvEmail <- o .:? "receiver_email"
+    recvEmailNorm <- o .:? "receiver_email_normalized"
+
+    let receiver = ReceivedByUser <$> recvId <*> recvEmail <*> recvEmailNorm
+
     pure Push
       <*> pure d
       <*> o .:? "source_device_iden"
@@ -190,14 +209,9 @@
       <*> o .: "modified"
       <*> o .: "dismissed"
       <*> o .: "direction"
-      <*> o .: "sender_iden"
-      <*> o .: "sender_email"
-      <*> o .: "sender_email_normalized"
-      <*> o .: "sender_name"
-      <*> o .: "receiver_iden"
-      <*> o .: "receiver_email"
-      <*> o .: "receiver_email_normalized"
-      <*> pure origin
+      <*> pure sender
+      <*> pure receiver
+
   parseJSON _ = fail "cannot parse push from non-object"
 
 instance FromJSON ExistingPushes where
@@ -230,7 +244,7 @@
       LinkPush{..} ->
         [ "type" .= id @Text "link"
         , "title" .= pushTitle
-        , "body" .= pushBody
+        , "body" .= pushLinkBody
         , "url" .= pushUrl
         ]
       FilePush{..} ->
