diff --git a/src/Web/Telegram/Types/Interaction.hs b/src/Web/Telegram/Types/Interaction.hs
--- a/src/Web/Telegram/Types/Interaction.hs
+++ b/src/Web/Telegram/Types/Interaction.hs
@@ -12,9 +12,11 @@
     CallbackQuery (..),
     ShippingQuery (..),
     PreCheckoutQuery (..),
+
     -- ** replys
     ReplyKeyboardMarkup (..),
     KeyboardButton (..),
+    KeyboardButtonAddon (..),
     KeyboardButtonPollType (..),
     ReplyKeyboardRemove (..),
     InlineKeyboardMarkup (..),
@@ -30,9 +32,9 @@
 import Data.OpenUnion
 import Deriving.Aeson
 import Servant.API
+import Web.Telegram.Types.Internal.Common
 import Web.Telegram.Types.Internal.Keyboard
 import Web.Telegram.Types.Internal.Utils
-import Web.Telegram.Types.Internal.Common
 
 type ReplyMarkup =
   Union
diff --git a/src/Web/Telegram/Types/Internal/Common.hs b/src/Web/Telegram/Types/Internal/Common.hs
--- a/src/Web/Telegram/Types/Internal/Common.hs
+++ b/src/Web/Telegram/Types/Internal/Common.hs
@@ -12,6 +12,7 @@
 
 import Control.Applicative
 import Data.Aeson
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -27,7 +28,7 @@
   | Group
   | Supergroup
   | Channel
-  deriving (Show, Eq, Ord, Generic, Default)
+  deriving (Show, Eq, Ord, Enum, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] ChatType
@@ -51,7 +52,7 @@
         stickerSetName :: Maybe Integer,
         canSetStickerSet :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "chat" Chat
@@ -62,7 +63,7 @@
       { metadata :: MessageMetadata,
         content :: MessageContent
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize Message
 
 instance FromJSON Message where
@@ -96,7 +97,7 @@
         authorSignature :: Maybe Text,
         replyMarkup :: Maybe InlineKeyboardMarkup
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, FieldLabelModifier CamelToSnake] MessageMetadata
@@ -198,7 +199,7 @@
   | PassportData
       { passPortData :: PassportData
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, FieldLabelModifier CamelToSnake] MessageContent
@@ -220,7 +221,7 @@
   | Pre
   | TextLink
   | TextMention
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Ord, Enum, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] MessageEntityType
@@ -235,7 +236,7 @@
         user :: Maybe User,
         language :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "entity" MessageEntity
@@ -251,7 +252,7 @@
         callbackData :: Maybe Text,
         gameShortName :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "callback" CallbackQuery
@@ -264,7 +265,7 @@
         bigFileId :: Text,
         bitFileUniqueId :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ChatPhoto
@@ -277,7 +278,7 @@
   | Restricted
   | Left
   | Kicked
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Ord, Enum, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] ChatStatus
@@ -305,7 +306,7 @@
         canSendOtherMesssages :: Maybe Bool,
         canAddWebPagePreviews :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ChatMember
@@ -322,7 +323,7 @@
         canInviteUsers :: Maybe Bool,
         canPinMessages :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ChatPermissions
@@ -333,7 +334,7 @@
       { command :: Text,
         description :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via OmitNothing BotCommand
@@ -344,7 +345,7 @@
       { label :: Text,
         amount :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via OmitNothing LabeledPrice
@@ -358,7 +359,7 @@
         currency :: Text,
         totalAmount :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Invoice
@@ -373,7 +374,7 @@
         streetLine2 :: Text,
         postCode :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ShippingAddress
@@ -386,7 +387,7 @@
         email :: Maybe Text,
         shippingAddress :: ShippingAddress
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake OrderInfo
@@ -398,7 +399,7 @@
         title :: Text,
         prices :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "option" ShippingOption
@@ -414,7 +415,7 @@
         telegramPaymentChargeId :: Text,
         providerPaymentChargeId :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake SuccessfulPayment
@@ -427,7 +428,7 @@
         invoicePayload :: Text,
         shippingAddress :: ShippingAddress
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "query" ShippingQuery
@@ -443,7 +444,7 @@
         shippingOptionId :: Maybe String,
         orderInfo :: Maybe OrderInfo
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "query" PreCheckoutQuery
@@ -458,7 +459,7 @@
         textEntities :: Maybe MessageEntity,
         animation :: Animation
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Game
@@ -472,7 +473,7 @@
         user :: User,
         score :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via OmitNothing GameHighScore
@@ -483,7 +484,7 @@
       { migrateToChatId :: Maybe Integer,
         retryAfter :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ResponseParameters
diff --git a/src/Web/Telegram/Types/Internal/InlineQuery.hs b/src/Web/Telegram/Types/Internal/InlineQuery.hs
--- a/src/Web/Telegram/Types/Internal/InlineQuery.hs
+++ b/src/Web/Telegram/Types/Internal/InlineQuery.hs
@@ -7,6 +7,7 @@
 module Web.Telegram.Types.Internal.InlineQuery where
 
 import Data.Aeson
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -24,7 +25,7 @@
         query :: Text,
         offset :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake' "query" InlineQuery
@@ -267,7 +268,7 @@
         replyMarkup :: Maybe InlineKeyboardMarkup,
         inputMessageContent :: Maybe InputMessageContent
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "result" InlineQueryResult
@@ -281,7 +282,7 @@
         inlineMessageId :: Maybe Text,
         query :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ChosenInlineResult
diff --git a/src/Web/Telegram/Types/Internal/InputFile.hs b/src/Web/Telegram/Types/Internal/InputFile.hs
--- a/src/Web/Telegram/Types/Internal/InputFile.hs
+++ b/src/Web/Telegram/Types/Internal/InputFile.hs
@@ -14,6 +14,7 @@
 
 import Data.ByteString.Lazy (ByteString)
 import qualified Data.ByteString.Lazy as LBS
+import Data.Hashable
 import Data.Proxy
 import Data.Text (Text, pack)
 import Data.Text.Encoding
@@ -30,7 +31,7 @@
         mimeType :: Text,
         content :: ByteString
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
 
 readInput :: FilePath -> IO InputFile
 readInput fp = do
@@ -58,53 +59,53 @@
       }
 
 newtype Cert = Cert InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "certificate"
 
 newtype Thumb = Thumb InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "thumb"
 
 newtype Photo = Photo InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "photo"
 
 newtype Audio = Audio InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "audio"
 
 newtype Doc = Doc InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "document"
 
 newtype Video = Video InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "video"
 
 newtype Animation = Animation InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "animation"
 
 newtype Voice = Voice InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "voice"
 
 newtype VideoNote = VideoNote InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "video_note"
 
 newtype Sticker = Sticker InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "sticker"
 
 newtype PngSticker = PngSticker InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "png_sticker"
 
 newtype TgsSticker = TgsSticker InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "tgs_sticker"
 
 newtype Media = Media InputFile
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (Multi) via InputF "media"
diff --git a/src/Web/Telegram/Types/Internal/InputMedia.hs b/src/Web/Telegram/Types/Internal/InputMedia.hs
--- a/src/Web/Telegram/Types/Internal/InputMedia.hs
+++ b/src/Web/Telegram/Types/Internal/InputMedia.hs
@@ -14,6 +14,7 @@
 module Web.Telegram.Types.Internal.InputMedia where
 
 import Data.Aeson
+import Data.Hashable
 import Data.Maybe (catMaybes)
 import Data.OpenUnion
 import Data.Text (Text)
@@ -27,7 +28,7 @@
   = MarkdownV2
   | HTML
   | Markdown
-  deriving (Show, Eq, Ord, Generic, Default)
+  deriving (Show, Eq, Ord, Enum, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via OmitNothing ParseMode
@@ -88,7 +89,7 @@
         height :: Maybe Integer,
         duration :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize InputMediaAnimation
 
 (.=?) :: ToJSON v => Text -> Maybe v -> Maybe (Text, Value)
@@ -128,7 +129,7 @@
         performer :: Maybe Text,
         title :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize InputMediaAudio
 
 instance ToJSON InputMediaAudio where
@@ -162,7 +163,7 @@
         caption :: Maybe Text,
         parseMode :: Maybe ParseMode
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize InputMediaDocument
 
 instance ToJSON InputMediaDocument where
@@ -192,7 +193,7 @@
         caption :: Maybe Text,
         parseMode :: Maybe ParseMode
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize InputMediaPhoto
 
 instance ToJSON InputMediaPhoto where
@@ -222,7 +223,7 @@
         caption :: Maybe Text,
         parseMode :: Maybe ParseMode
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving (ToHttpApiData) via Serialize InputMediaVideo
 
 instance ToJSON InputMediaVideo where
@@ -271,7 +272,7 @@
         lastName :: Maybe Text,
         vcard :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake InputMessageContent
diff --git a/src/Web/Telegram/Types/Internal/Keyboard.hs b/src/Web/Telegram/Types/Internal/Keyboard.hs
--- a/src/Web/Telegram/Types/Internal/Keyboard.hs
+++ b/src/Web/Telegram/Types/Internal/Keyboard.hs
@@ -3,9 +3,14 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DerivingVia #-}
 {-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
 
 module Web.Telegram.Types.Internal.Keyboard where
 
+import Data.Aeson
+import Data.Aeson.Types
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -19,30 +24,40 @@
         oneTimeKeyboard :: Maybe Bool,
         selective :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
-    (FromJSON, ToJSON)
+    (ToJSON)
     via Snake ReplyKeyboardMarkup
   deriving (ToHttpApiData) via Serialize ReplyKeyboardMarkup
 
 data KeyboardButton
   = KeyboardButton
       { text :: Text,
-        requestContact :: Maybe Bool,
-        requestLocation :: Maybe Bool,
-        requestPoll :: Maybe KeyboardButtonPollType
+        addon :: Maybe KeyboardButtonAddon
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
+  deriving (ToHttpApiData) via Serialize KeyboardButton
+
+instance ToJSON KeyboardButton where
+  toJSON KeyboardButton {..} =
+    let Object t = object ["text" .= text]
+        Object a = maybe emptyObject toJSON addon
+     in Object $ t <> a
+
+data KeyboardButtonAddon
+  = RequestContact Bool
+  | RequestLocation Bool
+  | RequestPoll KeyboardButtonPollType
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
-    via Snake KeyboardButton
-  deriving (ToHttpApiData) via Serialize KeyboardButton
+    via CustomJSON '[ConstructorTagModifier CamelToSnake, SumObjectWithSingleField] KeyboardButtonAddon
 
 newtype KeyboardButtonPollType
   = KeyboardButtonPollType
       { pollType :: Maybe PollType
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "poll" KeyboardButtonPollType
@@ -53,7 +68,7 @@
       { removeKeyboard :: Bool,
         selective :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ReplyKeyboardRemove
@@ -63,7 +78,7 @@
   = InlineKeyboardMarkup
       { inlineKeyboard :: [[InlineKeyboardButton]]
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (ToJSON, FromJSON)
     via Snake InlineKeyboardMarkup
@@ -79,7 +94,7 @@
         switchInlineQueryCurrentChat :: Maybe Text,
         pay :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake InlineKeyboardButton
@@ -92,7 +107,7 @@
         botUsername :: Maybe Text,
         requestWriteAccess :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake LoginUrl
@@ -103,7 +118,7 @@
       { forceReply :: Bool,
         selective :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake ForceReply
diff --git a/src/Web/Telegram/Types/Internal/Media.hs b/src/Web/Telegram/Types/Internal/Media.hs
--- a/src/Web/Telegram/Types/Internal/Media.hs
+++ b/src/Web/Telegram/Types/Internal/Media.hs
@@ -7,6 +7,7 @@
 module Web.Telegram.Types.Internal.Media where
 
 import Data.Aeson
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -21,7 +22,7 @@
         height :: Integer,
         fileSize :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake PhotoSize
@@ -38,7 +39,7 @@
         fileSize :: Maybe Integer,
         thumb :: Maybe PhotoSize
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Audio
@@ -53,7 +54,7 @@
         mimeType :: Maybe Text,
         fileSize :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Document
@@ -70,7 +71,7 @@
         mimeType :: Maybe Text,
         fileSize :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Video
@@ -88,7 +89,7 @@
         mimeType :: Maybe Text,
         fileSize :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Animation
@@ -102,7 +103,7 @@
         mimeType :: Maybe Text,
         fileSize :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Voice
@@ -117,7 +118,7 @@
         thumb :: Maybe PhotoSize,
         fileSize :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake VideoNote
@@ -131,7 +132,7 @@
         userId :: Maybe Integer,
         vcard :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Contact
@@ -142,7 +143,7 @@
       { longitude :: Float,
         latitude :: Float
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via OmitNothing Location
@@ -156,7 +157,7 @@
         foursquareId :: Maybe Text,
         foursquareType :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Venue
@@ -167,7 +168,7 @@
       { text :: Text,
         voterCount :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake PollOption
@@ -179,7 +180,7 @@
         user :: User,
         optionIds :: [Integer]
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "poll" PollAnswer
@@ -188,7 +189,7 @@
 data PollType
   = Regular
   | Quiz
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] PollType
@@ -206,7 +207,7 @@
         allowsMultipleAnswers :: Bool,
         correctOptionId :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "poll" Poll
@@ -217,7 +218,7 @@
       { totalCount :: Integer,
         photos :: [[PhotoSize]]
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake UserProfilePhotos
@@ -230,7 +231,7 @@
         fileSize :: Maybe Integer,
         filePath :: Maybe Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake File
diff --git a/src/Web/Telegram/Types/Internal/Passport.hs b/src/Web/Telegram/Types/Internal/Passport.hs
--- a/src/Web/Telegram/Types/Internal/Passport.hs
+++ b/src/Web/Telegram/Types/Internal/Passport.hs
@@ -6,6 +6,7 @@
 
 module Web.Telegram.Types.Internal.Passport where
 
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -16,7 +17,7 @@
       { passportData :: [EncryptedPassportElement],
         credentials :: EncryptedCredentials
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake "passport" PassportData
@@ -29,7 +30,7 @@
         fileSize :: Integer,
         fileDate :: Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (ToJSON, FromJSON)
     via Snake PassportFile
@@ -48,7 +49,7 @@
         translation :: Maybe [PassportFile],
         hash :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (ToJSON, FromJSON)
     via PrefixedSnake "element" EncryptedPassportElement
@@ -59,7 +60,7 @@
         hash :: Text,
         secret :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (ToJSON, FromJSON)
     via PrefixedSnake "credential" EncryptedCredentials
@@ -120,7 +121,7 @@
         elementHash :: Text,
         message :: Text
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (ToJSON, FromJSON)
     via PrefixedSnake "error" PassportElementError
@@ -140,7 +141,7 @@
   | TemporaryRegistration
   | PhoneNumber
   | Email
-  deriving (Show, Eq, Ord, Generic, Default)
+  deriving (Show, Eq, Ord, Enum, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] EncryptedPassportElementType
diff --git a/src/Web/Telegram/Types/Internal/Sticker.hs b/src/Web/Telegram/Types/Internal/Sticker.hs
--- a/src/Web/Telegram/Types/Internal/Sticker.hs
+++ b/src/Web/Telegram/Types/Internal/Sticker.hs
@@ -5,6 +5,7 @@
 
 module Web.Telegram.Types.Internal.Sticker where
 
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -24,7 +25,7 @@
         maskPosition :: Maybe MaskPosition,
         fileSize :: Maybe Integer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake Sticker
@@ -38,7 +39,7 @@
         containsMasks :: Bool,
         stickers :: [Sticker]
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake StickerSet
@@ -51,7 +52,7 @@
         yShift :: Float,
         scale :: Float
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake MaskPosition
diff --git a/src/Web/Telegram/Types/Internal/Update.hs b/src/Web/Telegram/Types/Internal/Update.hs
--- a/src/Web/Telegram/Types/Internal/Update.hs
+++ b/src/Web/Telegram/Types/Internal/Update.hs
@@ -8,6 +8,7 @@
 module Web.Telegram.Types.Internal.Update where
 
 import Data.Aeson
+import Data.Hashable
 import Data.Monoid
 import Data.Text (Text)
 import Deriving.Aeson
@@ -74,7 +75,7 @@
       { updateId :: Integer,
         answer :: M.PollAnswer
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
 
 instance FromJSON Update where
   parseJSON = withObject "Update object" $ \o -> do
@@ -119,7 +120,7 @@
         -- | A list of update types the bot is subscribed to. Defaults to all update types
         allowedUpdates :: Maybe [UpdateType]
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via Snake WebhookInfo
diff --git a/src/Web/Telegram/Types/Internal/UpdateType.hs b/src/Web/Telegram/Types/Internal/UpdateType.hs
--- a/src/Web/Telegram/Types/Internal/UpdateType.hs
+++ b/src/Web/Telegram/Types/Internal/UpdateType.hs
@@ -5,6 +5,7 @@
 
 module Web.Telegram.Types.Internal.UpdateType where
 
+import Data.Hashable
 import Deriving.Aeson
 import Web.Telegram.Types.Internal.Utils
 
@@ -20,7 +21,7 @@
   | PreCheckoutQuery
   | PollUpdate
   | PollAnswer
-  deriving (Show, Eq, Generic, Default, Enum)
+  deriving (Show, Eq, Generic, Default, Ord, Enum, Hashable)
   deriving
     (FromJSON, ToJSON)
     via CustomJSON '[SumUntaggedValue, ConstructorTagModifier CamelToSnake] UpdateType
diff --git a/src/Web/Telegram/Types/Internal/User.hs b/src/Web/Telegram/Types/Internal/User.hs
--- a/src/Web/Telegram/Types/Internal/User.hs
+++ b/src/Web/Telegram/Types/Internal/User.hs
@@ -11,6 +11,7 @@
 where
 
 import Data.Aeson
+import Data.Hashable
 import Data.Text (Text)
 import Deriving.Aeson
 import Servant.API
@@ -38,7 +39,7 @@
         -- | True, if the bot supports inline queries. Returned only in getMe.
         supportsInlineQueries :: Maybe Bool
       }
-  deriving (Show, Eq, Generic, Default)
+  deriving (Show, Eq, Generic, Default, Hashable)
   deriving
     (FromJSON, ToJSON)
     via PrefixedSnake' "user" User
diff --git a/telegram-types.cabal b/telegram-types.cabal
--- a/telegram-types.cabal
+++ b/telegram-types.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 864acb9c3962c6315f72ae625c1288a5d3467a6e49cd845e0da1d053f4bbe14e
+-- hash: fa0715f82fba82b6c5df10eb271dcfbf056c6b45435ed0b9ff2855ec5c4c1c99
 
 name:           telegram-types
-version:        0.2.2
+version:        0.3.0
 synopsis:       Types used in Telegram bot API
 description:    Defines various datatypes and their serialization methods useful for writing bindings to Telegram bot API.
 category:       Web
@@ -62,6 +62,7 @@
     , deriving-aeson >=0.2.3 && <0.3
     , filepath >=1.4.2.1 && <1.5
     , generic-lens >=1.2.0.1 && <1.3
+    , hashable >=1.3.0.0 && <1.4
     , lens >=4.18.1 && <4.19
     , mime-types >=0.1.0.9 && <0.2
     , open-union >=0.4.0.0 && <0.5
@@ -85,6 +86,7 @@
     , deriving-aeson >=0.2.3 && <0.3
     , filepath >=1.4.2.1 && <1.5
     , generic-lens >=1.2.0.1 && <1.3
+    , hashable >=1.3.0.0 && <1.4
     , lens >=4.18.1 && <4.19
     , mime-types >=0.1.0.9 && <0.2
     , open-union >=0.4.0.0 && <0.5
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,13 +1,19 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 module Main where
 
 import Data.Aeson
 import qualified Data.ByteString.Lazy as LBS
+import Web.Telegram.Types
 import Web.Telegram.Types.Input
+import Web.Telegram.Types.Interaction
 import Web.Telegram.Types.Update
-import Web.Telegram.Types
 
 main :: IO ()
 main = do
   f <- LBS.readFile "test/Message1"
   print $ (eitherDecode f :: Either String Update)
   LBS.putStrLn $ encode (def :: InputMedia)
+  putStrLn "==== Testring KeyboardButton ====="
+  LBS.putStrLn $ encode $ KeyboardButton "button" (Just $ RequestContact True)
+  LBS.putStrLn $ encode $ KeyboardButton "button2" Nothing
