diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,16 @@
 # Revision history for pushbullet-types
 
+## 0.2.0.0  -- 2017-04-30
+
+* Some fields in `PushData` are now made optional, since the objects received
+  from Pushbullet may have them missing.
+* A minimum value for `PushbulletTime` is added. This is just zero seconds
+  since the POSIX epoch.
+* Ephemerals are reworked. Now `Ephemeral` actually has one constructor per
+  ephemeral type, and the previous constructors of `Ephemeral` are moved into
+  `PushEphemeral`. `Ephemeral` now has a constructor for tickles and nops.
+  Only push-type tickles have a dedicated constructor in `TickleType`.
+
 ## 0.1.0.0  -- 2017-02-20
 
 Define the following core types:
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.1.0.0
+version:             0.2.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.1.0.0
-  tag: v0.1.0.0
+  location: https://github.com/tsani/pushbullet-types/releases/tag/v0.2.0.0
+  tag: v0.2.0.0
 
 library
   default-language:    Haskell2010
diff --git a/src/Network/Pushbullet/Types.hs b/src/Network/Pushbullet/Types.hs
--- a/src/Network/Pushbullet/Types.hs
+++ b/src/Network/Pushbullet/Types.hs
@@ -10,6 +10,8 @@
 , ExistingPushes(..)
   -- * Ephemerals
 , Ephemeral(..)
+, PushEphemeral(..)
+, TickleType(..)
 , Notification(..)
   -- * Devices
 , Device(..)
@@ -47,6 +49,7 @@
 , TrivialObject(..)
 , Status(..)
 , PushbulletTime(..)
+, minPushbulletTime
 , PhoneNumber(..)
 , Name(..)
 
diff --git a/src/Network/Pushbullet/Types/Ephemeral.hs b/src/Network/Pushbullet/Types/Ephemeral.hs
--- a/src/Network/Pushbullet/Types/Ephemeral.hs
+++ b/src/Network/Pushbullet/Types/Ephemeral.hs
@@ -28,7 +28,27 @@
 
 makeLenses ''Notification
 
+data TickleType
+  = PushType
+  | OtherType Text
+
+instance ToJSON TickleType where
+  toJSON t = case t of
+    PushType -> "push"
+    OtherType t' -> toJSON t'
+
+instance FromJSON TickleType where
+  parseJSON (String s) = pure $ case s of
+    "push" -> PushType
+    _ -> OtherType s
+  parseJSON _ = fail "cannot parse tickle type from non-string"
+
 data Ephemeral
+  = PushEphemeral PushEphemeral
+  | Nop
+  | Tickle TickleType
+
+data PushEphemeral
   = Sms
     { _ephSmsSourceUser :: !UserId
     , _ephSmsTargetDevice :: !DeviceId
@@ -46,62 +66,70 @@
     }
   deriving (Eq, Show)
 
-makeLenses ''Ephemeral
+makeLenses ''PushEphemeral
 
 instance ToJSON Ephemeral where
   toJSON o = case o of
-    Sms{..} -> object
+    Nop -> object
+      [ "type" .= id @Text "nop"
+      ]
+    Tickle subtype -> object
+      [ "type" .= id @Text "tickle"
+      , "subtype" .= subtype
+      ]
+    PushEphemeral pushEphemeral -> object
       [ "type" .= id @Text "push"
-      , "push" .= object
-        [ "type" .= id @Text "messaging_extension_reply"
-        , "package_name" .= id @Text "com.pushbullet.android"
-        , "source_user_iden" .= _ephSmsSourceUser
-        , "target_device_iden" .= _ephSmsTargetDevice
-        , "conversation_iden" .= _ephSmsConversation
-        , "message" .= _ephSmsMessage
-        ]
+      , "push" .= pushEphemeral
       ]
+
+instance ToJSON PushEphemeral where
+  toJSON o = case o of
+    Sms{..} ->  object
+      [ "type" .= id @Text "messaging_extension_reply"
+      , "package_name" .= id @Text "com.pushbullet.android"
+      , "source_user_iden" .= _ephSmsSourceUser
+      , "target_device_iden" .= _ephSmsTargetDevice
+      , "conversation_iden" .= _ephSmsConversation
+      , "message" .= _ephSmsMessage
+      ]
     Clipboard{..} -> object
-      [ "type" .= id @Text "push"
-      , "push" .= object
-        [ "type" .= id @Text "clip"
-        , "body" .= _ephClipBody
-        , "source_user_iden" .= _ephClipSourceUser
-        , "source_device_iden" .= _ephClipSourceDevice
-        ]
+      [ "type" .= id @Text "clip"
+      , "body" .= _ephClipBody
+      , "source_user_iden" .= _ephClipSourceUser
+      , "source_device_iden" .= _ephClipSourceDevice
       ]
     SmsChanged{..} -> object
-      [ "type" .= id @Text "push"
-      , "push" .= object
-        [ "type" .= id @Text "sms_changed"
-        , "source_device_iden" .= _ephSourceDevice
-        , "notifications" .= _ephNotifications
-        ]
+      [ "type" .= id @Text "sms_changed"
+      , "source_device_iden" .= _ephSourceDevice
+      , "notifications" .= _ephNotifications
       ]
 
 instance FromJSON Ephemeral where
   parseJSON (Object o) = do
     t <- o .: "type"
-    guard (t == id @Text "push")
-    p <- o .: "push"
-    case p of
-      Object p' -> do
-        t' <- p' .: "type"
-        case t' of
-          "messaging_extension_reply" -> pure Sms
-            <*> p' .: "source_user_iden"
-            <*> p' .: "target_device_iden"
-            <*> p' .: "conversation_iden"
-            <*> p' .: "message"
-          "clip" -> pure Clipboard
-            <*> p' .: "body"
-            <*> p' .: "source_user_iden"
-            <*> p' .: "source_device_iden"
-          "sms_changed" -> pure SmsChanged
-            <*> p' .: "source_device_iden"
-            <*> p' .: "notifications"
-          _ -> fail $ "unknown push type " <> t'
-      _ -> fail "cannot parse push from non-object"
+    case id @Text t of
+      "nop" -> pure Nop
+      "tickle" -> Tickle <$> o .: "subtype"
+      "push" -> PushEphemeral <$> o .: "push"
+      _ -> fail "unknown ephemeral type"
+
+instance FromJSON PushEphemeral where
+  parseJSON (Object o) = do
+    t' <- o .: "type"
+    case t' of
+      "messaging_extension_reply" -> pure Sms
+        <*> o .: "source_user_iden"
+        <*> o .: "target_device_iden"
+        <*> o .: "conversation_iden"
+        <*> o .: "message"
+      "clip" -> pure Clipboard
+        <*> o .: "body"
+        <*> o .: "source_user_iden"
+        <*> o .: "source_device_iden"
+      "sms_changed" -> pure SmsChanged
+        <*> o .: "source_device_iden"
+        <*> o .: "notifications"
+      _ -> fail $ "unknown push type " <> t'
 
 instance ToJSON Notification where
   toJSON n = object
diff --git a/src/Network/Pushbullet/Types/Misc.hs b/src/Network/Pushbullet/Types/Misc.hs
--- a/src/Network/Pushbullet/Types/Misc.hs
+++ b/src/Network/Pushbullet/Types/Misc.hs
@@ -19,7 +19,7 @@
 newtype MimeType = MimeType Text
   deriving (Eq, FromJSON, Show, ToJSON)
 
-newtype Url = Url Text
+newtype Url = Url { unUrl :: Text }
   deriving (Eq, FromJSON, Show, ToJSON)
 
 newtype Guid = Guid Text
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
@@ -71,20 +71,20 @@
 -- | The actual contents of a push.
 data PushData (s :: Status)
   = NotePush
-    { pushTitle :: !Text
+    { pushTitle :: !(Maybe Text)
     , pushBody :: !Text
     }
   | LinkPush
-    { pushTitle :: !Text
+    { pushTitle :: !(Maybe Text)
     , pushBody :: !Text
     , pushUrl :: !Url
     }
   | FilePush
-    { pushBody :: !Text
+    { pushFileBody :: !(Maybe Text)
     , pushFileName :: !Text
     , pushFileType :: !MimeType
     , pushFileUrl :: !Url
-    , pushFileTitle :: !(EqT 'Existing s Text)
+    , pushFileTitle :: !(EqT 'Existing s (Maybe Text))
     , pushImageUrl :: !(EqT 'Existing s (Maybe Url))
     , pushImageWidth :: !(EqT 'Existing s (Maybe Int))
     , pushImageHeight :: !(EqT 'Existing s (Maybe Int))
@@ -99,7 +99,10 @@
 -- | 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.
-newtype ExistingPushes = ExistingPushes [Push 'Existing]
+newtype ExistingPushes
+  = ExistingPushes
+    { unExistingPushes :: [Push 'Existing]
+    }
   deriving (Eq, Show)
 
 -- | Constructs a new @Push@ with the source device and guid set to @Nothing@.
@@ -156,19 +159,19 @@
     pushType <- o .: "type"
     d <- case id @Text pushType of
       "note" -> pure NotePush
-        <*> o .: "title"
+        <*> o .:? "title"
         <*> o .: "body"
       "file" -> pure FilePush
         <*> o .: "body"
         <*> o .: "file_name"
         <*> o .: "file_type"
         <*> o .: "file_url"
-        <*> o .: "file_title"
+        <*> o .:? "file_title"
         <*> o .:? "image_url"
         <*> o .:? "image_width"
         <*> o .:? "image_height"
       "link" -> pure LinkPush
-        <*> o .: "title"
+        <*> o .:? "title"
         <*> o .: "body"
         <*> o .: "url"
       _ -> fail "unrecognized push type"
@@ -232,7 +235,7 @@
         ]
       FilePush{..} ->
         [ "type" .= id @Text "file"
-        , "body" .= pushBody
+        , "body" .= pushFileBody
         , "file_name" .= pushFileName
         , "file_type" .= pushFileType
         , "file_url" .= pushFileUrl
diff --git a/src/Network/Pushbullet/Types/Time.hs b/src/Network/Pushbullet/Types/Time.hs
--- a/src/Network/Pushbullet/Types/Time.hs
+++ b/src/Network/Pushbullet/Types/Time.hs
@@ -8,7 +8,10 @@
 import Web.HttpApiData ( ToHttpApiData(..) )
 
 newtype PushbulletTime = PushbulletTime UTCTime
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Read, Show)
+
+minPushbulletTime :: PushbulletTime
+minPushbulletTime = PushbulletTime (posixSecondsToUTCTime 0)
 
 instance ToHttpApiData PushbulletTime where
   toUrlPiece (PushbulletTime utc) = fromString (show d) where
