diff --git a/CHANGELOG.md b/CHANGELOG.md
index 34f2e5d1..daaa61c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed
 
+ - Updated `teloxide-core` from version `0.4.5` to version [`0.5.0`](https://github.com/teloxide/teloxide-core/releases/tag/v0.5.0) [**BC**]
  - Rename `dispatching2` => `dispatching` [**BC**].
  - Rename `prelude2` => `prelude` [**BC**].
  - Move `update_listeners`, `stop_token`, `IdleShutdownError`, and `ShutdownToken` from the old `dispatching` to the new `dispatching` (previously `dispatching2`).
diff --git a/Cargo.toml b/Cargo.toml
index 91225a17..013f97bd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -56,7 +56,7 @@ full = [
 ]
 
 [dependencies]
-teloxide-core = { version = "0.4", default-features = false }
+teloxide-core = { version = "0.5", default-features = false }
 teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros.git", rev = "dfba097c7146ba6244a36001d703e04b771baa05", optional = true }
 
 serde_json = "1.0"
diff --git a/examples/dispatching_features.rs b/examples/dispatching_features.rs
index beda128c..fb36a96a 100644
--- a/examples/dispatching_features.rs
+++ b/examples/dispatching_features.rs
@@ -5,7 +5,7 @@ use rand::Rng;
 
 use teloxide::{
     prelude::*,
-    types::{Dice, Update},
+    types::{Dice, Update, UserId},
     utils::command::BotCommands,
 };
 
@@ -17,7 +17,7 @@ async fn main() {
     let bot = Bot::from_env().auto_send();
 
     let parameters = ConfigParameters {
-        bot_maintainer: 268486177, // Paste your ID to run this bot.
+        bot_maintainer: UserId(0), // Paste your ID to run this bot.
         maintainer_username: None,
     };
 
@@ -95,7 +95,7 @@ async fn main() {
 
 #[derive(Clone)]
 struct ConfigParameters {
-    bot_maintainer: i64,
+    bot_maintainer: UserId,
     maintainer_username: Option<String>,
 }
 
diff --git a/src/dispatching/dialogue/get_chat_id.rs b/src/dispatching/dialogue/get_chat_id.rs
index eb2a1bf1..d84d73fd 100644
--- a/src/dispatching/dialogue/get_chat_id.rs
+++ b/src/dispatching/dialogue/get_chat_id.rs
@@ -1,20 +1,20 @@
 use crate::types::CallbackQuery;
-use teloxide_core::types::Message;
+use teloxide_core::types::{ChatId, Message};
 
 /// Something that may has a chat ID.
 pub trait GetChatId {
     #[must_use]
-    fn chat_id(&self) -> Option<i64>;
+    fn chat_id(&self) -> Option<ChatId>;
 }
 
 impl GetChatId for Message {
-    fn chat_id(&self) -> Option<i64> {
+    fn chat_id(&self) -> Option<ChatId> {
         Some(self.chat.id)
     }
 }
 
 impl GetChatId for CallbackQuery {
-    fn chat_id(&self) -> Option<i64> {
+    fn chat_id(&self) -> Option<ChatId> {
         self.message.as_ref().map(|mes| mes.chat.id)
     }
 }
diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs
index 33d6f3ad..5f9e27f4 100644
--- a/src/dispatching/dialogue/mod.rs
+++ b/src/dispatching/dialogue/mod.rs
@@ -85,6 +85,7 @@ pub use crate::dispatching::dialogue::{SqliteStorage, SqliteStorageError};
 
 pub use get_chat_id::GetChatId;
 pub use storage::*;
+use teloxide_core::types::ChatId;
 
 use std::{marker::PhantomData, sync::Arc};
 
@@ -98,7 +99,7 @@ where
     S: ?Sized,
 {
     storage: Arc<S>,
-    chat_id: i64,
+    chat_id: ChatId,
     _phantom: PhantomData<D>,
 }
 
@@ -121,13 +122,13 @@ where
     /// Constructs a new dialogue with `storage` (where dialogues are stored)
     /// and `chat_id` of a current dialogue.
     #[must_use]
-    pub fn new(storage: Arc<S>, chat_id: i64) -> Self {
+    pub fn new(storage: Arc<S>, chat_id: ChatId) -> Self {
         Self { storage, chat_id, _phantom: PhantomData }
     }
 
     /// Returns a chat ID associated with this dialogue.
     #[must_use]
-    pub fn chat_id(&self) -> i64 {
+    pub fn chat_id(&self) -> ChatId {
         self.chat_id
     }
 
diff --git a/src/dispatching/dialogue/storage/in_mem_storage.rs b/src/dispatching/dialogue/storage/in_mem_storage.rs
index d26a21eb..5085b5b4 100644
--- a/src/dispatching/dialogue/storage/in_mem_storage.rs
+++ b/src/dispatching/dialogue/storage/in_mem_storage.rs
@@ -1,6 +1,7 @@
 use super::Storage;
 use futures::future::BoxFuture;
 use std::{collections::HashMap, sync::Arc};
+use teloxide_core::types::ChatId;
 use thiserror::Error;
 use tokio::sync::Mutex;
 
@@ -20,7 +21,7 @@ pub enum InMemStorageError {
 /// [`super::SqliteStorage`] or implement your own.
 #[derive(Debug)]
 pub struct InMemStorage<D> {
-    map: Mutex<HashMap<i64, D>>,
+    map: Mutex<HashMap<ChatId, D>>,
 }
 
 impl<S> InMemStorage<S> {
@@ -37,7 +38,10 @@ where
 {
     type Error = InMemStorageError;
 
-    fn remove_dialogue(self: Arc<Self>, chat_id: i64) -> BoxFuture<'static, Result<(), Self::Error>>
+    fn remove_dialogue(
+        self: Arc<Self>,
+        chat_id: ChatId,
+    ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
         D: Send + 'static,
     {
@@ -52,7 +56,7 @@ where
 
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
@@ -66,7 +70,7 @@ where
 
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
         Box::pin(async move { Ok(self.map.lock().await.get(&chat_id).map(ToOwned::to_owned)) })
     }
diff --git a/src/dispatching/dialogue/storage/mod.rs b/src/dispatching/dialogue/storage/mod.rs
index 600cf988..b78cd72e 100644
--- a/src/dispatching/dialogue/storage/mod.rs
+++ b/src/dispatching/dialogue/storage/mod.rs
@@ -10,6 +10,7 @@ mod redis_storage;
 mod sqlite_storage;
 
 use futures::future::BoxFuture;
+use teloxide_core::types::ChatId;
 
 pub use self::{
     in_mem_storage::{InMemStorage, InMemStorageError},
@@ -55,7 +56,7 @@ pub trait Storage<D> {
     #[must_use = "Futures are lazy and do nothing unless polled with .await"]
     fn remove_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
         D: Send + 'static;
@@ -64,7 +65,7 @@ pub trait Storage<D> {
     #[must_use = "Futures are lazy and do nothing unless polled with .await"]
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
@@ -74,7 +75,7 @@ pub trait Storage<D> {
     #[must_use = "Futures are lazy and do nothing unless polled with .await"]
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>>;
 
     /// Erases [`Self::Error`] to [`std::error::Error`].
@@ -97,7 +98,10 @@ where
 {
     type Error = Box<dyn std::error::Error + Send + Sync>;
 
-    fn remove_dialogue(self: Arc<Self>, chat_id: i64) -> BoxFuture<'static, Result<(), Self::Error>>
+    fn remove_dialogue(
+        self: Arc<Self>,
+        chat_id: ChatId,
+    ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
         D: Send + 'static,
     {
@@ -108,7 +112,7 @@ where
 
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
@@ -121,7 +125,7 @@ where
 
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
         Box::pin(
             async move { Arc::clone(&self.0).get_dialogue(chat_id).await.map_err(|e| e.into()) },
@@ -135,7 +139,7 @@ mod tests {
 
     #[tokio::test]
     async fn test_erased() {
-        let chat_id = 123;
+        let chat_id = ChatId(123);
 
         let erased = InMemStorage::new().erase();
         Arc::clone(&erased).update_dialogue(chat_id, 1).await.unwrap();
diff --git a/src/dispatching/dialogue/storage/redis_storage.rs b/src/dispatching/dialogue/storage/redis_storage.rs
index f3889acc..de9294d8 100644
--- a/src/dispatching/dialogue/storage/redis_storage.rs
+++ b/src/dispatching/dialogue/storage/redis_storage.rs
@@ -8,6 +8,7 @@ use std::{
     ops::DerefMut,
     sync::Arc,
 };
+use teloxide_core::types::ChatId;
 use thiserror::Error;
 use tokio::sync::Mutex;
 
@@ -56,7 +57,7 @@ where
 
     fn remove_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        ChatId(chat_id): ChatId,
     ) -> BoxFuture<'static, Result<(), Self::Error>> {
         Box::pin(async move {
             let deleted_rows_count = redis::pipe()
@@ -82,7 +83,7 @@ where
 
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        ChatId(chat_id): ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>> {
         Box::pin(async move {
@@ -95,7 +96,7 @@ where
 
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        ChatId(chat_id): ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
         Box::pin(async move {
             self.conn
diff --git a/src/dispatching/dialogue/storage/sqlite_storage.rs b/src/dispatching/dialogue/storage/sqlite_storage.rs
index a562b5e5..982fefc1 100644
--- a/src/dispatching/dialogue/storage/sqlite_storage.rs
+++ b/src/dispatching/dialogue/storage/sqlite_storage.rs
@@ -8,6 +8,7 @@ use std::{
     str,
     sync::Arc,
 };
+use teloxide_core::types::ChatId;
 use thiserror::Error;
 
 /// A persistent dialogue storage based on [SQLite](https://www.sqlite.org/).
@@ -66,7 +67,7 @@ where
     /// Returns [`sqlx::Error::RowNotFound`] if a dialogue does not exist.
     fn remove_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        ChatId(chat_id): ChatId,
     ) -> BoxFuture<'static, Result<(), Self::Error>> {
         Box::pin(async move {
             let deleted_rows_count =
@@ -86,7 +87,7 @@ where
 
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        ChatId(chat_id): ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>> {
         Box::pin(async move {
@@ -112,7 +113,7 @@ where
 
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
         Box::pin(async move {
             get_dialogue(&self.pool, chat_id)
@@ -123,7 +124,10 @@ where
     }
 }
 
-async fn get_dialogue(pool: &SqlitePool, chat_id: i64) -> Result<Option<Vec<u8>>, sqlx::Error> {
+async fn get_dialogue(
+    pool: &SqlitePool,
+    ChatId(chat_id): ChatId,
+) -> Result<Option<Vec<u8>>, sqlx::Error> {
     #[derive(sqlx::FromRow)]
     struct DialogueDbRow {
         dialogue: Vec<u8>,
diff --git a/src/dispatching/dialogue/storage/trace_storage.rs b/src/dispatching/dialogue/storage/trace_storage.rs
index f4e22d8e..33571194 100644
--- a/src/dispatching/dialogue/storage/trace_storage.rs
+++ b/src/dispatching/dialogue/storage/trace_storage.rs
@@ -5,6 +5,7 @@ use std::{
 };
 
 use futures::future::BoxFuture;
+use teloxide_core::types::ChatId;
 
 use crate::dispatching::dialogue::Storage;
 
@@ -34,7 +35,10 @@ where
 {
     type Error = <S as Storage<D>>::Error;
 
-    fn remove_dialogue(self: Arc<Self>, chat_id: i64) -> BoxFuture<'static, Result<(), Self::Error>>
+    fn remove_dialogue(
+        self: Arc<Self>,
+        chat_id: ChatId,
+    ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
         D: Send + 'static,
     {
@@ -44,7 +48,7 @@ where
 
     fn update_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
         dialogue: D,
     ) -> BoxFuture<'static, Result<(), Self::Error>>
     where
@@ -60,7 +64,7 @@ where
 
     fn get_dialogue(
         self: Arc<Self>,
-        chat_id: i64,
+        chat_id: ChatId,
     ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
         log::trace!("Requested a dialogue #{}", chat_id);
         <S as Storage<D>>::get_dialogue(self.inner.clone(), chat_id)
diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs
index fd194430..c6f14616 100644
--- a/src/dispatching/dispatcher.rs
+++ b/src/dispatching/dispatcher.rs
@@ -16,7 +16,10 @@ use std::{
     ops::{ControlFlow, Deref},
     sync::Arc,
 };
-use teloxide_core::{requests::Request, types::UpdateKind};
+use teloxide_core::{
+    requests::Request,
+    types::{ChatId, UpdateKind},
+};
 use tokio::time::timeout;
 use tokio_stream::wrappers::ReceiverStream;
 
@@ -102,7 +105,7 @@ pub struct Dispatcher<R, Err> {
     default_handler: DefaultHandler,
 
     // Tokio TX channel parts associated with chat IDs that consume updates sequentially.
-    workers: HashMap<i64, Worker>,
+    workers: HashMap<ChatId, Worker>,
     // The default TX part that consume updates concurrently.
     default_worker: Option<Worker>,
 
diff --git a/src/utils/html.rs b/src/utils/html.rs
index d61c84c2..11ae475d 100644
--- a/src/utils/html.rs
+++ b/src/utils/html.rs
@@ -93,6 +93,8 @@ pub fn user_mention_or_link(user: &User) -> String {
 
 #[cfg(test)]
 mod tests {
+    use teloxide_core::types::UserId;
+
     use super::*;
 
     #[test]
@@ -183,7 +185,7 @@ mod tests {
     #[test]
     fn user_mention_link() {
         let user_with_username = User {
-            id: 0,
+            id: UserId(0),
             is_bot: false,
             first_name: "".to_string(),
             last_name: None,
@@ -192,7 +194,7 @@ mod tests {
         };
         assert_eq!(user_mention_or_link(&user_with_username), "@abcd");
         let user_without_username = User {
-            id: 123_456_789,
+            id: UserId(123_456_789),
             is_bot: false,
             first_name: "Name".to_string(),
             last_name: None,
diff --git a/src/utils/markdown.rs b/src/utils/markdown.rs
index 6ee5f451..9b120d6a 100644
--- a/src/utils/markdown.rs
+++ b/src/utils/markdown.rs
@@ -131,7 +131,7 @@ pub fn user_mention_or_link(user: &User) -> String {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use teloxide_core::types::User;
+    use teloxide_core::types::{User, UserId};
 
     #[test]
     fn test_bold() {
@@ -234,7 +234,7 @@ mod tests {
     #[test]
     fn user_mention_link() {
         let user_with_username = User {
-            id: 0,
+            id: UserId(0),
             is_bot: false,
             first_name: "".to_string(),
             last_name: None,
@@ -243,7 +243,7 @@ mod tests {
         };
         assert_eq!(user_mention_or_link(&user_with_username), "@abcd");
         let user_without_username = User {
-            id: 123_456_789,
+            id: UserId(123_456_789),
             is_bot: false,
             first_name: "Name".to_string(),
             last_name: None,
diff --git a/tests/redis.rs b/tests/redis.rs
index dff537ec..f06375c8 100644
--- a/tests/redis.rs
+++ b/tests/redis.rs
@@ -2,7 +2,10 @@ use std::{
     fmt::{Debug, Display},
     sync::Arc,
 };
-use teloxide::dispatching::dialogue::{RedisStorage, RedisStorageError, Serializer, Storage};
+use teloxide::{
+    dispatching::dialogue::{RedisStorage, RedisStorageError, Serializer, Storage},
+    types::ChatId,
+};
 
 #[tokio::test]
 #[cfg_attr(not(CI_REDIS), ignore)]
@@ -44,9 +47,9 @@ type Dialogue = String;
 
 macro_rules! test_dialogues {
     ($storage:expr, $_0:expr, $_1:expr, $_2:expr) => {
-        assert_eq!(Arc::clone(&$storage).get_dialogue(1).await.unwrap(), $_0);
-        assert_eq!(Arc::clone(&$storage).get_dialogue(11).await.unwrap(), $_1);
-        assert_eq!(Arc::clone(&$storage).get_dialogue(256).await.unwrap(), $_2);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(1)).await.unwrap(), $_0);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(11)).await.unwrap(), $_1);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(256)).await.unwrap(), $_2);
     };
 }
 
@@ -57,9 +60,9 @@ where
 {
     test_dialogues!(storage, None, None, None);
 
-    Arc::clone(&storage).update_dialogue(1, "ABC".to_owned()).await.unwrap();
-    Arc::clone(&storage).update_dialogue(11, "DEF".to_owned()).await.unwrap();
-    Arc::clone(&storage).update_dialogue(256, "GHI".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(1), "ABC".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(11), "DEF".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(256), "GHI".to_owned()).await.unwrap();
 
     test_dialogues!(
         storage,
@@ -68,15 +71,15 @@ where
         Some("GHI".to_owned())
     );
 
-    Arc::clone(&storage).remove_dialogue(1).await.unwrap();
-    Arc::clone(&storage).remove_dialogue(11).await.unwrap();
-    Arc::clone(&storage).remove_dialogue(256).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(11)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(256)).await.unwrap();
 
     test_dialogues!(storage, None, None, None);
 
     // Check that a try to remove a non-existing dialogue results in an error.
     assert!(matches!(
-        Arc::clone(&storage).remove_dialogue(1).await.unwrap_err(),
+        Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap_err(),
         RedisStorageError::DialogueNotFound
     ));
 }
diff --git a/tests/sqlite.rs b/tests/sqlite.rs
index 18dec25b..96cec7db 100644
--- a/tests/sqlite.rs
+++ b/tests/sqlite.rs
@@ -3,7 +3,10 @@ use std::{
     fs,
     sync::Arc,
 };
-use teloxide::dispatching::dialogue::{Serializer, SqliteStorage, SqliteStorageError, Storage};
+use teloxide::{
+    dispatching::dialogue::{Serializer, SqliteStorage, SqliteStorageError, Storage},
+    types::ChatId,
+};
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_sqlite_json() {
@@ -48,9 +51,9 @@ type Dialogue = String;
 
 macro_rules! test_dialogues {
     ($storage:expr, $_0:expr, $_1:expr, $_2:expr) => {
-        assert_eq!(Arc::clone(&$storage).get_dialogue(1).await.unwrap(), $_0);
-        assert_eq!(Arc::clone(&$storage).get_dialogue(11).await.unwrap(), $_1);
-        assert_eq!(Arc::clone(&$storage).get_dialogue(256).await.unwrap(), $_2);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(1)).await.unwrap(), $_0);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(11)).await.unwrap(), $_1);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(256)).await.unwrap(), $_2);
     };
 }
 
@@ -61,9 +64,9 @@ where
 {
     test_dialogues!(storage, None, None, None);
 
-    Arc::clone(&storage).update_dialogue(1, "ABC".to_owned()).await.unwrap();
-    Arc::clone(&storage).update_dialogue(11, "DEF".to_owned()).await.unwrap();
-    Arc::clone(&storage).update_dialogue(256, "GHI".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(1), "ABC".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(11), "DEF".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(256), "GHI".to_owned()).await.unwrap();
 
     test_dialogues!(
         storage,
@@ -72,15 +75,15 @@ where
         Some("GHI".to_owned())
     );
 
-    Arc::clone(&storage).remove_dialogue(1).await.unwrap();
-    Arc::clone(&storage).remove_dialogue(11).await.unwrap();
-    Arc::clone(&storage).remove_dialogue(256).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(11)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(256)).await.unwrap();
 
     test_dialogues!(storage, None, None, None);
 
     // Check that a try to remove a non-existing dialogue results in an error.
     assert!(matches!(
-        Arc::clone(&storage).remove_dialogue(1).await.unwrap_err(),
+        Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap_err(),
         SqliteStorageError::DialogueNotFound
     ));
 }