Peers

Many constructors in the API need to be stored in a local database upon reception and should only ever be updated reactively (passively) when received via updates or by other means (as specified in the documentation), to avoid overloading the server by continuously requesting changes for the same unchanged information.

user, chat, channel constructors and their full counterparts userFull, chatFull, channelFull are especially important, because they contain important information about users, bots, chats and channels (from now on, peers), and most importantly the access_hash value, required to interact with peers in the API.

This page describes exactly how and when should the local databases of the constructors listed above be refreshed, and contains a more detailed description of basic peer-related concepts.

For simplicity, the documentation often uses the term "cache" instead of "database" when referring to the peer database, however note that the peer database is not an ephemeral cache: peer information (or at the very least the id+access hash) must be persisted to a database and never deleted, to avoid losing access to the API, which requires the locally stored access hash to call methods.

Peer info database

userEmpty#d3bc4b7a id:long = User;
user#215c4438 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User;

chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat;

channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;

inputUserEmpty#b98886cf = InputUser;
inputUserSelf#f7c1b13f = InputUser;
inputUser#f21158c6 user_id:long access_hash:long = InputUser;
inputUserFromMessage#1da448e2 peer:InputPeer msg_id:int user_id:long = InputUser;

// No inputChat, just a long is used (because basic chats don't have access hashes)

inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
inputChannelFromMessage#5b934f9d peer:InputPeer msg_id:int channel_id:long = InputChannel;

inputPeerEmpty#7f3b18ea = InputPeer;
inputPeerSelf#7da07ec9 = InputPeer;
inputPeerChat#35a95cb9 chat_id:long = InputPeer;
inputPeerUser#dde8a54c user_id:long access_hash:long = InputPeer;
inputPeerChannel#27bcbbfc channel_id:long access_hash:long = InputPeer;
inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer;
inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer;

---functions---

users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
messages.getChats#49e9528f id:Vector<long> = messages.Chats;
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;

The peer info database needs to be updated every time a new constructor of type user, chat and channel (and their forbidden counterparts, used for peers that the user can't access, but can view basic info about) is received.

These constructors are received when interacting with the API (i.e. in common chats, through the search function, username resolution, temporary profile links, and so on...).

Unless specified otherwise (see the constructor pages for the special cases), when updating the local peer database, all fields from the newly received constructor take priority over the old constructor cached locally (including by removing fields that aren't set in the new constructor).

Information about already cached peers must be refreshed in certain conditions using the bulked users.getUsers, messages.getChats, channels.getChannels methods, all requiring the previously cached access_hash (read on for more info).

Peer info must be refreshed only in the following conditions (i.e. do not refresh user info when opening the profile page, and so on...):

  • When passively receiving an updateUser, updateChat, updateChannel, and some other updates, as specified here »
  • When fetching info about one of the following internal user IDs, if the info isn't already cached or if the cached info is min: 777000 (service notifications user), 1271266957 (replies bot), 1087968824 (anonymous bot), 136817688 (channel bot), 5434988373 (antispam bot).
    Info about these IDs may be fetched with the zero access hash even by users.
  • After invoking bots.setBotInfo after changing name (but not the about or the description, as changing those fields already triggers a refresh of the full info database, and with it the peer database), for the bot whose info we changed
  • After receiving a CHAT_FORWARDS_RESTRICTED error when forwarding messages to a chat

Do not refresh peer information in any other case, to avoid overloading the server by continuously requesting changes for the same unchanged information.

The peer info database contains the following information:

Peer ID

The peer id is a unique 64-bit ID used to identify a specific user, chat or channel.

This field should be used as primary key in the channel, chat and user databases.

Note that the ID sequences of users, chats and channels overlap, so you must either:

  • Use separate tables/hashmaps for users, chats and channels OR
  • Transform the peer IDs to bot API IDs as specified here », which will allow you to use a single ID sequence (and database) for all three peer types, maintaining uniqueness.
    In this case, a single table can be used for all peer types, but since the structures of the constructors are different, to avoid useless typechecks it might be a good idea to use three tables, as with the first approach.

It's a good idea to transform peer IDs to bot API IDs even if you do decide to use separate databases, as it will make IDs more visually recognizable both for you and your users, as well as guarantee compatibility with the bot API.

Access hash

The access_hash is, the second most important field stored in the peer database, used to generate InputPeer, inputUser, inputChannel constructors used to interact with peers in the API.
Note that chats (basic groups ») do not have or need an access hash.
users and channels (supergroups and channels ») have an access hash, and it can come in various flavors:

  • Full access hash: can be used everywhere in the API.
  • Min access hash: received from min constructors », can only be used to fetch the profile pictures using inputPeerPhotoFileLocation ».
  • From-message access hash: not a real access hash, constructed as specified here », must be used when only a min access hash is available locally, but a full access hash is required.
  • Zero access hash: equal to 0, must be used by bots when only a min access hash (or no access hash) is available locally, but a full access hash is required.

The access hash versions listed above are listed in descending priority, and if a version with higher priority is currently cached, it must not be overwritten with a lower priority version.

Access hashes are received when interacting with the API (i.e. in common chats, through the search function, username resolution, temporary profile links, and so on...): if you have only a user/channel/supergroup ID without any kind of access hash, you cannot interact with that peer.
Access hashes may not be reused across different accounts or different sessions of the same account.
This is a core spam prevention feature of Telegram.

Clients and client APIs should not expose access hashes to users, as they cannot be reused outside of the current session, and the user should not be burdened with storing them, when the client can perfectly do the job by itself.

For simplicity, the documentation often uses the term "cache" instead of "database" when referring to the peer database, however note that the peer database is not an ephemeral cache: peer information (or at the very least the id+access hash) must be persisted to a database and never deleted, to avoid losing access to the API, which requires the locally stored access hash to call methods.

Other info

Various other fields commonly used by the client, as specified in the constructor pages (user, chat and channel).
As specified in the constructor docs, some of the fields must not be overwritten if a min constructor is received, and a change in some other fields must trigger invalidation of the full info database ».

Full info database

users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;
messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector<Chat> users:Vector<User> = messages.ChatFull;

userFull#cc997720 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int = UserFull;
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;

---functions---

users.getFullUser#b60f5918 id:InputUser = users.UserFull;
messages.getFullChat#aeb00b34 chat_id:long = messages.ChatFull;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;

The full info database needs to be updated every time a new constructor of type userFull, chatFull and channelFull is received, by completely replacing the locally cached entry.

These constructors may only be received with a manual call to