2025-01-18 02:58:43 +02:00
syntax = "proto3" ;
package signal . backup ;
2026-04-13 16:32:10 +03:00
option java_package = "org.signal.archive.proto" ;
2025-01-18 02:58:43 +02:00
option swift_prefix = "BackupProto_" ;
message BackupInfo {
uint64 version = 1 ;
uint64 backupTimeMs = 2 ;
bytes mediaRootBackupKey = 3 ; // 32-byte random value generated when the backup is uploaded for the first time.
string currentAppVersion = 4 ;
string firstAppVersion = 5 ;
2025-07-23 11:18:43 +03:00
bytes debugInfo = 6 ; // Client-specific data field for debug info during testing
2025-01-18 02:58:43 +02:00
}
// Frames must follow in the following ordering rules:
//
// 1. There is exactly one AccountData and it is the first frame.
// 2. A frame referenced by ID must come before the referencing frame.
// e.g. a Recipient must come before any Chat referencing it.
// 3. All ChatItems must appear in global Chat rendering order.
// (The order in which they were received by the client.)
// 4. ChatFolders must appear in render order (e.g., left to right for
// LTR locales), but can appear anywhere relative to other frames respecting
// rule 2 (after Recipients and Chats).
//
// Recipients, Chats, StickerPacks, AdHocCalls, and NotificationProfiles
// can be in any order. (But must respect rule 2.)
//
// For example, Chats may all be together at the beginning,
// or may each immediately precede its first ChatItem.
message Frame {
2025-01-30 12:41:05 +02:00
// If unset, importers should skip this frame without throwing an error.
2025-01-18 02:58:43 +02:00
oneof item {
AccountData account = 1 ;
Recipient recipient = 2 ;
Chat chat = 3 ;
ChatItem chatItem = 4 ;
StickerPack stickerPack = 5 ;
AdHocCall adHocCall = 6 ;
NotificationProfile notificationProfile = 7 ;
ChatFolder chatFolder = 8 ;
}
}
message AccountData {
enum PhoneNumberSharingMode {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Nobody"
2025-01-18 02:58:43 +02:00
EVERYBODY = 1 ;
NOBODY = 2 ;
}
message UsernameLink {
enum Color {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Blue"
2025-01-18 02:58:43 +02:00
BLUE = 1 ;
WHITE = 2 ;
GREY = 3 ;
OLIVE = 4 ;
GREEN = 5 ;
ORANGE = 6 ;
PINK = 7 ;
PURPLE = 8 ;
}
bytes entropy = 1 ; // 32 bytes of entropy used for encryption
bytes serverId = 2 ; // 16 bytes of encoded UUID provided by the server
Color color = 3 ;
}
2026-02-09 14:13:18 +02:00
enum SentMediaQuality {
UNKNOWN_QUALITY = 0 ; // Interpret as "Standard"
STANDARD = 1 ;
HIGH = 2 ;
}
message AutoDownloadSettings {
enum AutoDownloadOption {
UNKNOWN = 0 ; // Interpret as "Never"
NEVER = 1 ;
WIFI = 2 ;
WIFI_AND_CELLULAR = 3 ;
}
AutoDownloadOption images = 1 ;
AutoDownloadOption audio = 2 ;
AutoDownloadOption video = 3 ;
AutoDownloadOption documents = 4 ;
}
enum AppTheme {
UNKNOWN_APP_THEME = 0 ; // Interpret as "System"
SYSTEM = 1 ;
LIGHT = 2 ;
DARK = 3 ;
}
enum CallsUseLessDataSetting {
UNKNOWN_CALL_DATA_SETTING = 0 ; // Interpret as "Never"
NEVER = 1 ;
MOBILE_DATA_ONLY = 2 ;
WIFI_AND_MOBILE_DATA = 3 ;
}
2025-01-18 02:58:43 +02:00
message AccountSettings {
2026-09-04 22:59:22 +03:00
enum UnreadBadgeType {
UNKNOWN_BADGE_TYPE = 0 ; // Interpret as "Unread messages"
UNREAD_MESSAGES = 1 ;
UNREAD_CHATS = 2 ;
}
2025-01-18 02:58:43 +02:00
bool readReceipts = 1 ;
bool sealedSenderIndicators = 2 ;
bool typingIndicators = 3 ;
bool linkPreviews = 4 ;
bool notDiscoverableByPhoneNumber = 5 ;
bool preferContactAvatars = 6 ;
uint32 universalExpireTimerSeconds = 7 ; // 0 means no universal expire timer.
repeated string preferredReactionEmoji = 8 ;
bool displayBadgesOnProfile = 9 ;
bool keepMutedChatsArchived = 10 ;
bool hasSetMyStoriesPrivacy = 11 ;
bool hasViewedOnboardingStory = 12 ;
bool storiesDisabled = 13 ;
optional bool storyViewReceiptsEnabled = 14 ;
bool hasSeenGroupStoryEducationSheet = 15 ;
bool hasCompletedUsernameOnboarding = 16 ;
PhoneNumberSharingMode phoneNumberSharingMode = 17 ;
ChatStyle defaultChatStyle = 18 ;
repeated ChatStyle.CustomChatColor customChatColors = 19 ;
2025-06-30 13:22:34 +03:00
bool optimizeOnDeviceStorage = 20 ;
// See zkgroup for integer particular values. Unset if backups are not enabled.
optional uint64 backupTier = 21 ;
2026-02-09 14:13:18 +02:00
reserved /* showSealedSenderIndicators */ 22 ;
SentMediaQuality defaultSentMediaQuality = 23 ;
AutoDownloadSettings autoDownloadSettings = 24 ;
reserved /* wifiAutoDownloadSettings */ 25 ;
optional uint32 screenLockTimeoutMinutes = 26 ; // If unset, consider screen lock to be disabled.
optional bool pinReminders = 27 ; // If unset, consider pin reminders to be enabled.
AppTheme appTheme = 28 ; // If unset, treat the same as "Unknown" case
CallsUseLessDataSetting callsUseLessDataSetting = 29 ; // If unset, treat the same as "Unknown" case
bool allowSealedSenderFromAnyone = 30 ;
bool allowAutomaticKeyVerification = 31 ;
2026-04-13 16:32:10 +03:00
bool hasSeenAdminDeleteEducationDialog = 32 ;
2026-09-04 22:59:22 +03:00
UnreadBadgeType unreadBadgeType = 33 ; // Only used in ios/desktop
optional bool includeMutedChatsInBadge = 34 ; // Only used in ios/desktop. If unset, consider this disabled
optional bool reactionNotifications = 35 ; // If unset, consider this enabled
optional bool notifyForCallsIfMuted = 36 ; // If unset, consider this disabled
optional bool notifyForMentionsIfMuted = 37 ; // If unset, consider this enabled
optional bool notifyForRepliesIfMuted = 38 ; // If unset, consider this enabled
optional bool showUnreadReminders = 39 ; // If unset, consider this enabled
optional bool notifyWhenContactJoins = 40 ; // If unset, consider this disabled
2025-01-18 02:58:43 +02:00
}
message SubscriberData {
bytes subscriberId = 1 ;
string currencyCode = 2 ;
bool manuallyCancelled = 3 ;
}
message IAPSubscriberData {
bytes subscriberId = 1 ;
2025-01-30 12:41:05 +02:00
// If unset, importers should ignore the subscriber data without throwing an error.
2025-01-18 02:58:43 +02:00
oneof iapSubscriptionId {
// Identifies an Android Play Store IAP subscription.
string purchaseToken = 2 ;
// Identifies an iOS App Store IAP subscription.
uint64 originalTransactionId = 3 ;
}
}
2026-02-09 14:13:18 +02:00
message AndroidSpecificSettings {
enum NavigationBarSize {
UNKNOWN_BAR_SIZE = 0 ; // Intepret as "Normal"
NORMAL = 1 ;
COMPACT = 2 ;
}
bool useSystemEmoji = 1 ;
bool screenshotSecurity = 2 ;
NavigationBarSize navigationBarSize = 3 ; // If unset, treat the same as "Unknown" case
}
2026-10-01 18:12:17 +03:00
message IOSSpecificSettings {
bool isSystemCallLogEnabled = 1 ;
}
2025-01-18 02:58:43 +02:00
bytes profileKey = 1 ;
optional string username = 2 ;
UsernameLink usernameLink = 3 ;
string givenName = 4 ;
string familyName = 5 ;
string avatarUrlPath = 6 ;
SubscriberData donationSubscriberData = 7 ;
reserved /*backupsSubscriberData*/ 8 ; // A deprecated format
AccountSettings accountSettings = 9 ;
IAPSubscriberData backupsSubscriberData = 10 ;
2025-04-15 15:22:31 +03:00
string svrPin = 11 ;
2026-02-09 14:13:18 +02:00
AndroidSpecificSettings androidSpecificSettings = 12 ;
string bioText = 13 ;
string bioEmoji = 14 ;
2026-10-01 18:12:17 +03:00
reserved /*keyTransparencyData*/ 15 ;
IOSSpecificSettings iosSpecificSettings = 16 ;
2025-01-18 02:58:43 +02:00
}
message Recipient {
uint64 id = 1 ; // generated id for reference only within this file
2025-01-30 12:41:05 +02:00
// If unset, importers should skip this frame without throwing an error.
2025-01-18 02:58:43 +02:00
oneof destination {
Contact contact = 2 ;
Group group = 3 ;
DistributionListItem distributionList = 4 ;
Self self = 5 ;
ReleaseNotes releaseNotes = 6 ;
CallLink callLink = 7 ;
}
}
2025-04-15 15:22:31 +03:00
// If unset - computed as the value of the first byte of SHA-256(msg=CONTACT_ID)
// modulo the count of colors. Once set the avatar color for a recipient is
// never recomputed or changed.
//
// `CONTACT_ID` is the first available identifier from the list:
// - ServiceIdToBinary(ACI)
// - E164
// - ServiceIdToBinary(PNI)
// - Group Id
enum AvatarColor {
A100 = 0 ;
A110 = 1 ;
A120 = 2 ;
A130 = 3 ;
A140 = 4 ;
A150 = 5 ;
A160 = 6 ;
A170 = 7 ;
A180 = 8 ;
A190 = 9 ;
A200 = 10 ;
A210 = 11 ;
}
2025-01-18 02:58:43 +02:00
message Contact {
enum IdentityState {
2025-01-30 12:41:05 +02:00
DEFAULT = 0 ; // A valid value -- indicates unset by the user
2025-01-18 02:58:43 +02:00
VERIFIED = 1 ;
2025-01-30 12:41:05 +02:00
UNVERIFIED = 2 ; // Was once verified and is now unverified
2025-01-18 02:58:43 +02:00
}
message Registered { }
message NotRegistered {
uint64 unregisteredTimestamp = 1 ;
}
enum Visibility {
2025-01-30 12:41:05 +02:00
VISIBLE = 0 ; // A valid value -- the contact is not hidden
2025-01-18 02:58:43 +02:00
HIDDEN = 1 ;
HIDDEN_MESSAGE_REQUEST = 2 ;
}
message Name {
string given = 1 ;
string family = 2 ;
}
optional bytes aci = 1 ; // should be 16 bytes
optional bytes pni = 2 ; // should be 16 bytes
optional string username = 3 ;
optional uint64 e164 = 4 ;
bool blocked = 5 ;
Visibility visibility = 6 ;
2025-01-30 12:41:05 +02:00
// If unset, consider the user to be registered
2025-01-18 02:58:43 +02:00
oneof registration {
Registered registered = 7 ;
NotRegistered notRegistered = 8 ;
}
optional bytes profileKey = 9 ;
bool profileSharing = 10 ;
optional string profileGivenName = 11 ;
optional string profileFamilyName = 12 ;
bool hideStory = 13 ;
optional bytes identityKey = 14 ;
IdentityState identityState = 15 ;
Name nickname = 16 ; // absent iff both `given` and `family` are empty
string note = 17 ;
2025-04-15 15:22:31 +03:00
string systemGivenName = 18 ;
string systemFamilyName = 19 ;
string systemNickname = 20 ;
optional AvatarColor avatarColor = 21 ;
2026-10-01 18:12:17 +03:00
// Opaque blob containing key transparency data for the contact
2026-02-09 14:13:18 +02:00
optional bytes keyTransparencyData = 22 ;
2026-09-04 22:59:22 +03:00
uint64 blockedAtTimestamp = 23 ; // if `blocked` is true, 0 means unknown block time
2026-10-01 18:12:17 +03:00
Name sharedName = 24 ; // absent iff both `given` and `family` are empty, name provided by third party
2025-01-18 02:58:43 +02:00
}
message Group {
enum StorySendMode {
2025-01-30 12:41:05 +02:00
DEFAULT = 0 ; // A valid value -- indicates unset by the user
2025-01-18 02:58:43 +02:00
DISABLED = 1 ;
ENABLED = 2 ;
}
bytes masterKey = 1 ;
bool whitelisted = 2 ;
bool hideStory = 3 ;
StorySendMode storySendMode = 4 ;
GroupSnapshot snapshot = 5 ;
2025-01-30 12:41:05 +02:00
bool blocked = 6 ;
2025-04-15 15:22:31 +03:00
optional AvatarColor avatarColor = 7 ;
2026-09-04 22:59:22 +03:00
uint64 blockedAtTimestamp = 8 ; // if `blocked` is true, 0 means unknown block time
2025-01-18 02:58:43 +02:00
// These are simply plaintext copies of the groups proto from Groups.proto.
// They should be kept completely in-sync with Groups.proto.
// These exist to allow us to have the latest snapshot of a group during restoration without having to hit the network.
// We would use Groups.proto if we could, but we want a plaintext version to improve export readability.
// For documentation, defer to Groups.proto. The only name change is Group -> GroupSnapshot to avoid the naming conflict.
message GroupSnapshot {
reserved /*publicKey*/ 1 ; // The field is deprecated in the context of static group state
GroupAttributeBlob title = 2 ;
GroupAttributeBlob description = 11 ;
string avatarUrl = 3 ;
GroupAttributeBlob disappearingMessagesTimer = 4 ;
AccessControl accessControl = 5 ;
uint32 version = 6 ;
repeated Member members = 7 ;
repeated MemberPendingProfileKey membersPendingProfileKey = 8 ;
repeated MemberPendingAdminApproval membersPendingAdminApproval = 9 ;
bytes inviteLinkPassword = 10 ;
2026-10-01 18:12:17 +03:00
bool announcementsOnly = 12 ;
repeated MemberBanned membersBanned = 13 ;
2026-04-13 16:32:10 +03:00
bool terminated = 14 ;
2025-01-18 02:58:43 +02:00
}
message GroupAttributeBlob {
2025-01-30 12:41:05 +02:00
// If unset, consider the field it represents to not be present
2025-01-18 02:58:43 +02:00
oneof content {
string title = 1 ;
bytes avatar = 2 ;
uint32 disappearingMessagesDuration = 3 ;
string descriptionText = 4 ;
}
}
message Member {
enum Role {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Intepret as "Default"
2025-01-18 02:58:43 +02:00
DEFAULT = 1 ;
ADMINISTRATOR = 2 ;
}
bytes userId = 1 ;
Role role = 2 ;
reserved /*profileKey*/ 3 ; // This field is ignored in Backups, in favor of Contact frames for members
reserved /*presentation*/ 4 ; // This field is deprecated in the context of static group state
uint32 joinedAtVersion = 5 ;
2026-04-13 16:32:10 +03:00
string labelEmoji = 6 ;
string labelString = 7 ;
2025-01-18 02:58:43 +02:00
}
message MemberPendingProfileKey {
Member member = 1 ;
bytes addedByUserId = 2 ;
uint64 timestamp = 3 ;
}
message MemberPendingAdminApproval {
bytes userId = 1 ;
reserved /*profileKey*/ 2 ; // This field is ignored in Backups, in favor of Contact frames for members
reserved /*presentation*/ 3 ; // This field is deprecated in the context of static group state
uint64 timestamp = 4 ;
}
message MemberBanned {
bytes userId = 1 ;
uint64 timestamp = 2 ;
}
message AccessControl {
enum AccessRequired {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Intepret as "Unsatisfiable"
2025-01-18 02:58:43 +02:00
ANY = 1 ;
MEMBER = 2 ;
ADMINISTRATOR = 3 ;
UNSATISFIABLE = 4 ;
}
AccessRequired attributes = 1 ;
AccessRequired members = 2 ;
AccessRequired addFromInviteLink = 3 ;
2026-04-13 16:32:10 +03:00
AccessRequired memberLabel = 4 ;
2025-01-18 02:58:43 +02:00
}
}
2025-04-15 15:22:31 +03:00
message Self {
optional AvatarColor avatarColor = 1 ;
}
2025-01-18 02:58:43 +02:00
message ReleaseNotes { }
message Chat {
uint64 id = 1 ; // generated id for reference only within this file
uint64 recipientId = 2 ;
bool archived = 3 ;
optional uint32 pinnedOrder = 4 ; // will be displayed in ascending order
optional uint64 expirationTimerMs = 5 ;
optional uint64 muteUntilMs = 6 ; // INT64_MAX (2^63 - 1) = "always muted".
bool markedUnread = 7 ;
2026-09-04 22:59:22 +03:00
bool dontNotifyForMentionsIfMuted = 8 ; // will be deprecated in favor of [notifyForMentionsIfMuted]
2025-01-18 02:58:43 +02:00
ChatStyle style = 9 ;
uint32 expireTimerVersion = 10 ;
2026-09-04 22:59:22 +03:00
optional bool notifyForCallsIfMuted = 11 ; // If unset, use default global settings
optional bool notifyForMentionsIfMuted = 12 ; // If unset, use default global settings. Only for groups. If [dontNotifyForMentionsIfMuted] is true, this should be initialized to false.
optional bool notifyForRepliesIfMuted = 13 ; // If unset, use default global settings. Only for groups.
optional bool showUnreadReminders = 14 ; // If unset, use default global settings
2025-01-18 02:58:43 +02:00
}
/ * *
* Call Links have some associated data including a call , but unlike other recipients
* are not tied to threads because they do not have messages associated with them.
*
* note :
* - room id can be derived from the root key
* - the presence of an admin key means this user is a call admin
* /
message CallLink {
enum Restrictions {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Admin Approval"
2025-01-18 02:58:43 +02:00
NONE = 1 ;
ADMIN_APPROVAL = 2 ;
}
bytes rootKey = 1 ;
optional bytes adminKey = 2 ; // Only present if the user is an admin
string name = 3 ;
Restrictions restrictions = 4 ;
uint64 expirationMs = 5 ;
2026-04-13 16:32:10 +03:00
reserved /*epoch*/ 6 ;
2025-01-18 02:58:43 +02:00
}
message AdHocCall {
enum State {
2025-01-30 12:41:05 +02:00
UNKNOWN_STATE = 0 ; // Interpret as "Generic"
2025-01-18 02:58:43 +02:00
GENERIC = 1 ;
}
uint64 callId = 1 ;
// Refers to a `CallLink` recipient.
uint64 recipientId = 2 ;
State state = 3 ;
uint64 callTimestamp = 4 ;
}
message DistributionListItem {
// distribution ids are UUIDv4s. "My Story" is represented
// by an all-0 UUID (00000000-0000-0000-0000-000000000000).
bytes distributionId = 1 ; // distribution list ids are uuids
2025-01-30 12:41:05 +02:00
// If unset, importers should skip the item entirely without showing an error.
2025-01-18 02:58:43 +02:00
oneof item {
uint64 deletionTimestamp = 2 ;
DistributionList distributionList = 3 ;
}
}
message DistributionList {
enum PrivacyMode {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Only with"
2025-01-18 02:58:43 +02:00
ONLY_WITH = 1 ;
ALL_EXCEPT = 2 ;
ALL = 3 ;
}
string name = 1 ;
bool allowReplies = 2 ;
PrivacyMode privacyMode = 3 ;
repeated uint64 memberRecipientIds = 4 ; // generated recipient id
}
message ChatItem {
message IncomingMessageDetails {
uint64 dateReceived = 1 ;
optional uint64 dateServerSent = 2 ;
bool read = 3 ;
bool sealedSender = 4 ;
}
message OutgoingMessageDetails {
repeated SendStatus sendStatus = 1 ;
2025-08-22 16:28:48 +03:00
uint64 dateReceived = 2 ; // may be different from dateSent for sync messages
2025-01-18 02:58:43 +02:00
}
message DirectionlessMessageDetails {
}
2026-02-09 14:13:18 +02:00
message PinDetails {
uint64 pinnedAtTimestamp = 1 ;
oneof pinExpiry {
uint64 pinExpiresAtTimestamp = 2 ; // timestamp when the pin should expire
bool pinNeverExpires = 3 ;
}
}
2025-01-18 02:58:43 +02:00
uint64 chatId = 1 ; // conversation id
uint64 authorId = 2 ; // recipient id
uint64 dateSent = 3 ;
optional uint64 expireStartDate = 4 ; // timestamp of when expiration timer started ticking down
optional uint64 expiresInMs = 5 ; // how long timer of message is (ms)
repeated ChatItem revisions = 6 ; // ordered from oldest to newest
bool sms = 7 ;
2025-01-30 12:41:05 +02:00
// If unset, importers should skip this item without throwing an error.
2025-01-18 02:58:43 +02:00
oneof directionalDetails {
IncomingMessageDetails incoming = 8 ;
OutgoingMessageDetails outgoing = 9 ;
DirectionlessMessageDetails directionless = 10 ;
}
2025-01-30 12:41:05 +02:00
// If unset, importers should skip this item without throwing an error.
2025-01-18 02:58:43 +02:00
oneof item {
StandardMessage standardMessage = 11 ;
ContactMessage contactMessage = 12 ;
StickerMessage stickerMessage = 13 ;
RemoteDeletedMessage remoteDeletedMessage = 14 ;
ChatUpdateMessage updateMessage = 15 ;
PaymentNotification paymentNotification = 16 ;
GiftBadge giftBadge = 17 ;
ViewOnceMessage viewOnceMessage = 18 ;
DirectStoryReplyMessage directStoryReplyMessage = 19 ; // group story reply messages are not backed up
2025-10-29 14:10:25 +02:00
Poll poll = 20 ;
2026-04-13 16:32:10 +03:00
AdminDeletedMessage adminDeletedMessage = 22 ;
2025-01-18 02:58:43 +02:00
}
2026-02-09 14:13:18 +02:00
PinDetails pinDetails = 21 ; // only set if message is pinned
2025-01-18 02:58:43 +02:00
}
message SendStatus {
message Pending { }
message Sent {
bool sealedSender = 1 ;
}
message Delivered {
bool sealedSender = 1 ;
}
message Read {
bool sealedSender = 1 ;
}
message Viewed {
bool sealedSender = 1 ;
}
// e.g. user in group was blocked, so we skipped sending to them
message Skipped { }
message Failed {
enum FailureReason {
UNKNOWN = 0 ; // A valid value -- could indicate a crash or lack of information
NETWORK = 1 ;
IDENTITY_KEY_MISMATCH = 2 ;
}
FailureReason reason = 1 ;
}
uint64 recipientId = 1 ;
uint64 timestamp = 2 ; // the time the status was last updated -- if from a receipt, it should be the sentTime of the receipt
2025-01-30 12:41:05 +02:00
// If unset, importers should consider the status to be "pending"
2025-01-18 02:58:43 +02:00
oneof deliveryStatus {
Pending pending = 3 ;
Sent sent = 4 ;
Delivered delivered = 5 ;
Read read = 6 ;
Viewed viewed = 7 ;
Skipped skipped = 8 ;
Failed failed = 9 ;
}
}
message Text {
string body = 1 ;
repeated BodyRange bodyRanges = 2 ;
}
message StandardMessage {
optional Quote quote = 1 ;
optional Text text = 2 ;
repeated MessageAttachment attachments = 3 ;
repeated LinkPreview linkPreview = 4 ;
optional FilePointer longText = 5 ;
repeated Reaction reactions = 6 ;
}
message ContactMessage {
ContactAttachment contact = 1 ;
repeated Reaction reactions = 2 ;
}
message DirectStoryReplyMessage {
message TextReply {
Text text = 1 ;
FilePointer longText = 2 ;
}
2025-01-30 12:41:05 +02:00
// If unset, importers should ignore the message without throwing an error.
2025-01-18 02:58:43 +02:00
oneof reply {
TextReply textReply = 1 ;
string emoji = 2 ;
}
repeated Reaction reactions = 3 ;
2025-01-30 12:41:05 +02:00
reserved /*storySentTimestamp*/ 4 ;
2025-01-18 02:58:43 +02:00
}
message PaymentNotification {
message TransactionDetails {
message MobileCoinTxoIdentification { // Used to map to payments on the ledger
repeated bytes publicKey = 1 ; // for received transactions
repeated bytes keyImages = 2 ; // for sent transactions
}
message FailedTransaction { // Failed payments can't be synced from the ledger
enum FailureReason {
2025-01-30 12:41:05 +02:00
GENERIC = 0 ; // A valid value -- reason unknown
2025-01-18 02:58:43 +02:00
NETWORK = 1 ;
INSUFFICIENT_FUNDS = 2 ;
}
FailureReason reason = 1 ;
}
message Transaction {
enum Status {
2025-01-30 12:41:05 +02:00
INITIAL = 0 ; // A valid value -- state unconfirmed
2025-01-18 02:58:43 +02:00
SUBMITTED = 1 ;
SUCCESSFUL = 2 ;
}
Status status = 1 ;
// This identification is used to map the payment table to the ledger
// and is likely required otherwise we may have issues reconciling with
// the ledger
MobileCoinTxoIdentification mobileCoinIdentification = 2 ;
optional uint64 timestamp = 3 ;
optional uint64 blockIndex = 4 ;
optional uint64 blockTimestamp = 5 ;
optional bytes transaction = 6 ; // mobile coin blobs
optional bytes receipt = 7 ; // mobile coin blobs
}
2025-01-30 12:41:05 +02:00
// If unset, importers should treat the transaction as successful with no metadata.
2025-01-18 02:58:43 +02:00
oneof payment {
Transaction transaction = 1 ;
FailedTransaction failedTransaction = 2 ;
}
}
optional string amountMob = 1 ; // stored as a decimal string, e.g. 1.00001
optional string feeMob = 2 ; // stored as a decimal string, e.g. 1.00001
optional string note = 3 ;
TransactionDetails transactionDetails = 4 ;
}
message GiftBadge {
enum State {
2025-01-30 12:41:05 +02:00
UNOPENED = 0 ; // A valid state
2025-01-18 02:58:43 +02:00
OPENED = 1 ;
REDEEMED = 2 ;
FAILED = 3 ;
}
bytes receiptCredentialPresentation = 1 ;
State state = 2 ;
}
message ViewOnceMessage {
// Will be null for viewed messages
MessageAttachment attachment = 1 ;
repeated Reaction reactions = 2 ;
}
message ContactAttachment {
message Name {
string givenName = 1 ;
string familyName = 2 ;
string prefix = 3 ;
string suffix = 4 ;
string middleName = 5 ;
string nickname = 6 ;
}
2026-10-01 18:12:17 +03:00
// The sharer's Signal nickname for this contact
message SignalNickname {
string given = 1 ;
string family = 2 ;
}
2025-01-18 02:58:43 +02:00
message Phone {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Home"
2025-01-18 02:58:43 +02:00
HOME = 1 ;
MOBILE = 2 ;
WORK = 3 ;
CUSTOM = 4 ;
}
string value = 1 ;
Type type = 2 ;
string label = 3 ;
}
message Email {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Intepret as "Home"
2025-01-18 02:58:43 +02:00
HOME = 1 ;
MOBILE = 2 ;
WORK = 3 ;
CUSTOM = 4 ;
}
string value = 1 ;
Type type = 2 ;
string label = 3 ;
}
message PostalAddress {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Home"
2025-01-18 02:58:43 +02:00
HOME = 1 ;
WORK = 2 ;
CUSTOM = 3 ;
}
Type type = 1 ;
string label = 2 ;
string street = 3 ;
string pobox = 4 ;
string neighborhood = 5 ;
string city = 6 ;
string region = 7 ;
string postcode = 8 ;
string country = 9 ;
}
optional Name name = 1 ;
2026-10-01 18:12:17 +03:00
reserved 2 ;
2025-01-18 02:58:43 +02:00
repeated Phone number = 3 ;
repeated Email email = 4 ;
repeated PostalAddress address = 5 ;
optional FilePointer avatar = 6 ;
string organization = 7 ;
2026-10-01 18:12:17 +03:00
bytes aci = 8 ; // should be 16 bytes
optional SignalNickname nickname = 9 ; // absent iff both `given` and `family` are empty
string note = 10 ;
2025-01-18 02:58:43 +02:00
}
message StickerMessage {
Sticker sticker = 1 ;
repeated Reaction reactions = 2 ;
}
// Tombstone for remote delete
message RemoteDeletedMessage { }
message Sticker {
bytes packId = 1 ;
bytes packKey = 2 ;
uint32 stickerId = 3 ;
optional string emoji = 4 ;
// Stickers are uploaded to be sent as attachments; we also
// back them up as normal attachments when they are in messages.
// DO NOT treat this as the definitive source of a sticker in
// an installed StickerPack that shares the same packId.
FilePointer data = 5 ;
}
message LinkPreview {
string url = 1 ;
optional string title = 2 ;
optional FilePointer image = 3 ;
optional string description = 4 ;
optional uint64 date = 5 ;
}
// A FilePointer on a message that has additional
// metadata that applies only to message attachments.
message MessageAttachment {
// Similar to SignalService.AttachmentPointer.Flags,
// but explicitly mutually exclusive. Note the different raw values
// (non-zero starting values are not supported in proto3.)
enum Flag {
2025-01-30 12:41:05 +02:00
NONE = 0 ; // A valid value -- no flag applied
2025-01-18 02:58:43 +02:00
VOICE_MESSAGE = 1 ;
BORDERLESS = 2 ;
GIF = 3 ;
}
FilePointer pointer = 1 ;
Flag flag = 2 ;
bool wasDownloaded = 3 ;
// Cross-client identifier for this attachment among all attachments on the
// owning message. See: SignalService.AttachmentPointer.clientUuid.
optional bytes clientUuid = 4 ;
}
message FilePointer {
2025-06-30 13:22:34 +03:00
message LocatorInfo {
// Must be non-empty if transitCdnKey or plaintextHash are set/nonempty.
// Otherwise must be empty.
bytes key = 1 ;
2025-08-22 16:28:48 +03:00
reserved /*legacyDigest*/ 2 ;
2025-06-30 13:22:34 +03:00
oneof integrityCheck {
// Set if file was at one point downloaded and its plaintextHash was calculated
bytes plaintextHash = 10 ;
// Set if file has not been downloaded so its integrity has not been verified
// From the sender of the attachment
bytes encryptedDigest = 11 ;
}
// NB: This is the plaintext size, and empty content attachments are legal, so this
// may be zero even if transitCdnKey or mediaName are set/nonempty.
uint32 size = 3 ;
// Either both transit cdn key and number are set or neither should be set.
// Upload timestamp is optional but should only be set if key/number are set.
optional string transitCdnKey = 4 ;
optional uint32 transitCdnNumber = 5 ;
optional uint64 transitTierUploadTimestamp = 6 ;
// If present, the cdn number of the succesful upload to media tier.
// If unset, may still have been uploaded, and clients
// can discover the cdn number via the list endpoint.
// Exporting clients should set this as long as their subscription
// has not rotated since last upload; even if currently free tier.
optional uint32 mediaTierCdnNumber = 7 ;
2025-08-22 16:28:48 +03:00
reserved /*legacyMediaName*/ 8 ;
2025-06-30 13:22:34 +03:00
// Separate key used to encrypt this file for the local backup.
// Generally required for local backups.
// Missing field indicates attachment was not available locally
// when the backup was generated, but remote backup or transit
// info was available.
optional bytes localKey = 9 ;
}
2025-08-22 16:28:48 +03:00
reserved /*backupLocator*/ 1 ;
reserved /*attachmentLocator*/ 2 ;
reserved /*invalidAttachmentLocator*/ 3 ;
reserved /*localLocator*/ 12 ;
2025-01-18 02:58:43 +02:00
optional string contentType = 4 ;
optional bytes incrementalMac = 5 ;
optional uint32 incrementalMacChunkSize = 6 ;
optional string fileName = 7 ;
optional uint32 width = 8 ;
optional uint32 height = 9 ;
optional string caption = 10 ;
optional string blurHash = 11 ;
2025-06-30 13:22:34 +03:00
LocatorInfo locatorInfo = 13 ;
2026-10-01 18:12:17 +03:00
// Waveform for audio attachments. Each byte represents a "bar" in the
// waveform, with 0 as silence 255 as loudest. At most 100 bytes/bars.
optional bytes audioWaveform = 14 ;
// Duration of an audio attachment, in seconds.
optional float audioDurationSeconds = 15 ;
2025-01-18 02:58:43 +02:00
}
message Quote {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Normal"
2025-01-18 02:58:43 +02:00
NORMAL = 1 ;
GIFT_BADGE = 2 ;
VIEW_ONCE = 3 ;
2025-10-29 14:10:25 +02:00
POLL = 4 ;
2025-01-18 02:58:43 +02:00
}
message QuotedAttachment {
optional string contentType = 1 ;
optional string fileName = 2 ;
optional MessageAttachment thumbnail = 3 ;
}
optional uint64 targetSentTimestamp = 1 ; // null if the target message could not be found at time of quote insert
uint64 authorId = 2 ;
optional Text text = 3 ;
repeated QuotedAttachment attachments = 4 ;
Type type = 5 ;
}
message BodyRange {
enum Style {
2025-01-30 12:41:05 +02:00
NONE = 0 ; // Importers should ignore the body range without throwing an error.
2025-01-18 02:58:43 +02:00
BOLD = 1 ;
ITALIC = 2 ;
SPOILER = 3 ;
STRIKETHROUGH = 4 ;
MONOSPACE = 5 ;
}
2025-01-30 12:41:05 +02:00
// 'start' and 'length' are measured in UTF-16 code units.
// They may refer to offsets in a longText attachment.
uint32 start = 1 ;
uint32 length = 2 ;
2025-01-18 02:58:43 +02:00
2025-01-30 12:41:05 +02:00
// If unset, importers should ignore the body range without throwing an error.
2025-01-18 02:58:43 +02:00
oneof associatedValue {
bytes mentionAci = 3 ;
Style style = 4 ;
}
}
message Reaction {
string emoji = 1 ;
uint64 authorId = 2 ;
uint64 sentTimestamp = 3 ;
// A higher sort order means that a reaction is more recent. Some clients may export this as
// incrementing numbers (e.g. 1, 2, 3), others as timestamps.
uint64 sortOrder = 4 ;
}
2025-10-29 14:10:25 +02:00
message Poll {
message PollOption {
message PollVote {
uint64 voterId = 1 ; // A direct reference to Recipient proto id. Must be self or contact.
uint32 voteCount = 2 ; // Tracks how many times you voted.
}
string option = 1 ; // Between 1-100 characters
repeated PollVote votes = 2 ;
}
string question = 1 ; // Between 1-100 characters
bool allowMultiple = 2 ;
repeated PollOption options = 3 ; // At least two
bool hasEnded = 4 ;
2026-02-09 14:13:18 +02:00
repeated Reaction reactions = 5 ;
2025-10-29 14:10:25 +02:00
}
2026-04-13 16:32:10 +03:00
message AdminDeletedMessage {
uint64 adminId = 1 ; // id of the admin that deleted the message
}
2025-01-18 02:58:43 +02:00
message ChatUpdateMessage {
2025-01-30 12:41:05 +02:00
// If unset, importers should ignore the update message without throwing an error.
2025-01-18 02:58:43 +02:00
oneof update {
SimpleChatUpdate simpleUpdate = 1 ;
GroupChangeChatUpdate groupChange = 2 ;
ExpirationTimerChatUpdate expirationTimerChange = 3 ;
ProfileChangeChatUpdate profileChange = 4 ;
ThreadMergeChatUpdate threadMerge = 5 ;
SessionSwitchoverChatUpdate sessionSwitchover = 6 ;
IndividualCall individualCall = 7 ;
GroupCall groupCall = 8 ;
LearnedProfileChatUpdate learnedProfileChange = 9 ;
2025-10-29 14:10:25 +02:00
PollTerminateUpdate pollTerminate = 10 ;
2026-02-09 14:13:18 +02:00
PinMessageUpdate pinMessage = 11 ;
2025-01-18 02:58:43 +02:00
}
}
message IndividualCall {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN_TYPE = 0 ; // Interpret as "Audio call"
2025-01-18 02:58:43 +02:00
AUDIO_CALL = 1 ;
VIDEO_CALL = 2 ;
}
enum Direction {
2025-01-30 12:41:05 +02:00
UNKNOWN_DIRECTION = 0 ; // Interpret as "Incoming"
2025-01-18 02:58:43 +02:00
INCOMING = 1 ;
OUTGOING = 2 ;
}
enum State {
2025-01-30 12:41:05 +02:00
UNKNOWN_STATE = 0 ; // Interpret as "Accepted"
2025-01-18 02:58:43 +02:00
ACCEPTED = 1 ;
NOT_ACCEPTED = 2 ;
// An incoming call that is no longer ongoing, which we neither accepted
// not actively declined. For example, it expired, was canceled by the
// sender, or was rejected due to being in another call.
MISSED = 3 ;
// We auto-declined an incoming call due to a notification profile.
MISSED_NOTIFICATION_PROFILE = 4 ;
}
optional uint64 callId = 1 ;
Type type = 2 ;
Direction direction = 3 ;
State state = 4 ;
uint64 startedCallTimestamp = 5 ;
bool read = 6 ;
}
message GroupCall {
enum State {
2025-01-30 12:41:05 +02:00
UNKNOWN_STATE = 0 ; // Interpret as "Generic"
2025-01-18 02:58:43 +02:00
// A group call was started without ringing.
GENERIC = 1 ;
// We joined a group call that was started without ringing.
JOINED = 2 ;
// An incoming group call is actively ringing.
RINGING = 3 ;
// We accepted an incoming group ring.
ACCEPTED = 4 ;
// We declined an incoming group ring.
DECLINED = 5 ;
// We missed an incoming group ring, for example because it expired.
MISSED = 6 ;
// We auto-declined an incoming group ring due to a notification profile.
MISSED_NOTIFICATION_PROFILE = 7 ;
// An outgoing ring was started. We don't track any state for outgoing rings
// beyond that they started.
OUTGOING_RING = 8 ;
}
optional uint64 callId = 1 ;
State state = 2 ;
optional uint64 ringerRecipientId = 3 ;
optional uint64 startedCallRecipientId = 4 ;
uint64 startedCallTimestamp = 5 ;
optional uint64 endedCallTimestamp = 6 ; // The time the call ended.
bool read = 7 ;
}
message SimpleChatUpdate {
enum Type {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Importers should skip the update without throwing an error.
2025-01-18 02:58:43 +02:00
JOINED_SIGNAL = 1 ;
IDENTITY_UPDATE = 2 ;
IDENTITY_VERIFIED = 3 ;
IDENTITY_DEFAULT = 4 ; // marking as unverified
CHANGE_NUMBER = 5 ;
RELEASE_CHANNEL_DONATION_REQUEST = 6 ;
END_SESSION = 7 ;
CHAT_SESSION_REFRESH = 8 ;
BAD_DECRYPT = 9 ;
PAYMENTS_ACTIVATED = 10 ;
PAYMENT_ACTIVATION_REQUEST = 11 ;
UNSUPPORTED_PROTOCOL_MESSAGE = 12 ;
REPORTED_SPAM = 13 ;
BLOCKED = 14 ;
UNBLOCKED = 15 ;
MESSAGE_REQUEST_ACCEPTED = 16 ;
}
Type type = 1 ;
}
// For 1:1 chat updates only.
// For group thread updates use GroupExpirationTimerUpdate.
message ExpirationTimerChatUpdate {
uint64 expiresInMs = 1 ; // 0 means the expiration timer was disabled
}
message ProfileChangeChatUpdate {
string previousName = 1 ;
string newName = 2 ;
}
message LearnedProfileChatUpdate {
2025-01-30 12:41:05 +02:00
// If unset, importers should consider the previous name to be an empty string.
2025-01-18 02:58:43 +02:00
oneof previousName {
uint64 e164 = 1 ;
string username = 2 ;
2026-10-01 18:12:17 +03:00
string sharedName = 3 ;
2025-01-18 02:58:43 +02:00
}
}
message ThreadMergeChatUpdate {
uint64 previousE164 = 1 ;
}
message SessionSwitchoverChatUpdate {
uint64 e164 = 1 ;
}
message GroupChangeChatUpdate {
message Update {
2025-01-30 12:41:05 +02:00
// If unset, importers should consider it to be a GenericGroupUpdate with unset updaterAci
2025-01-18 02:58:43 +02:00
oneof update {
GenericGroupUpdate genericGroupUpdate = 1 ;
GroupCreationUpdate groupCreationUpdate = 2 ;
GroupNameUpdate groupNameUpdate = 3 ;
GroupAvatarUpdate groupAvatarUpdate = 4 ;
GroupDescriptionUpdate groupDescriptionUpdate = 5 ;
GroupMembershipAccessLevelChangeUpdate groupMembershipAccessLevelChangeUpdate = 6 ;
GroupAttributesAccessLevelChangeUpdate groupAttributesAccessLevelChangeUpdate = 7 ;
GroupAnnouncementOnlyChangeUpdate groupAnnouncementOnlyChangeUpdate = 8 ;
GroupAdminStatusUpdate groupAdminStatusUpdate = 9 ;
GroupMemberLeftUpdate groupMemberLeftUpdate = 10 ;
GroupMemberRemovedUpdate groupMemberRemovedUpdate = 11 ;
SelfInvitedToGroupUpdate selfInvitedToGroupUpdate = 12 ;
SelfInvitedOtherUserToGroupUpdate selfInvitedOtherUserToGroupUpdate = 13 ;
GroupUnknownInviteeUpdate groupUnknownInviteeUpdate = 14 ;
GroupInvitationAcceptedUpdate groupInvitationAcceptedUpdate = 15 ;
GroupInvitationDeclinedUpdate groupInvitationDeclinedUpdate = 16 ;
GroupMemberJoinedUpdate groupMemberJoinedUpdate = 17 ;
GroupMemberAddedUpdate groupMemberAddedUpdate = 18 ;
GroupSelfInvitationRevokedUpdate groupSelfInvitationRevokedUpdate = 19 ;
GroupInvitationRevokedUpdate groupInvitationRevokedUpdate = 20 ;
GroupJoinRequestUpdate groupJoinRequestUpdate = 21 ;
GroupJoinRequestApprovalUpdate groupJoinRequestApprovalUpdate = 22 ;
GroupJoinRequestCanceledUpdate groupJoinRequestCanceledUpdate = 23 ;
GroupInviteLinkResetUpdate groupInviteLinkResetUpdate = 24 ;
GroupInviteLinkEnabledUpdate groupInviteLinkEnabledUpdate = 25 ;
GroupInviteLinkAdminApprovalUpdate groupInviteLinkAdminApprovalUpdate = 26 ;
GroupInviteLinkDisabledUpdate groupInviteLinkDisabledUpdate = 27 ;
GroupMemberJoinedByLinkUpdate groupMemberJoinedByLinkUpdate = 28 ;
GroupV2MigrationUpdate groupV2MigrationUpdate = 29 ;
GroupV2MigrationSelfInvitedUpdate groupV2MigrationSelfInvitedUpdate = 30 ;
GroupV2MigrationInvitedMembersUpdate groupV2MigrationInvitedMembersUpdate = 31 ;
GroupV2MigrationDroppedMembersUpdate groupV2MigrationDroppedMembersUpdate = 32 ;
GroupSequenceOfRequestsAndCancelsUpdate groupSequenceOfRequestsAndCancelsUpdate = 33 ;
GroupExpirationTimerUpdate groupExpirationTimerUpdate = 34 ;
2026-04-13 16:32:10 +03:00
GroupMemberLabelAccessLevelChangeUpdate groupMemberLabelAccessLevelChangeUpdate = 35 ;
GroupTerminateChangeUpdate groupTerminateChangeUpdate = 36 ;
2025-01-18 02:58:43 +02:00
}
}
// Must be one or more; all updates batched together came from
// a single batched group state update.
repeated Update updates = 1 ;
}
message GenericGroupUpdate {
optional bytes updaterAci = 1 ;
}
message GroupCreationUpdate {
optional bytes updaterAci = 1 ;
}
message GroupNameUpdate {
optional bytes updaterAci = 1 ;
// Null value means the group name was removed.
optional string newGroupName = 2 ;
}
message GroupAvatarUpdate {
optional bytes updaterAci = 1 ;
bool wasRemoved = 2 ;
}
message GroupDescriptionUpdate {
optional bytes updaterAci = 1 ;
// Null value means the group description was removed.
optional string newDescription = 2 ;
}
enum GroupV2AccessLevel {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Unsatisfiable"
2025-01-18 02:58:43 +02:00
ANY = 1 ;
MEMBER = 2 ;
ADMINISTRATOR = 3 ;
UNSATISFIABLE = 4 ;
}
message GroupMembershipAccessLevelChangeUpdate {
optional bytes updaterAci = 1 ;
GroupV2AccessLevel accessLevel = 2 ;
}
2026-04-13 16:32:10 +03:00
message GroupMemberLabelAccessLevelChangeUpdate {
optional bytes updaterAci = 1 ;
GroupV2AccessLevel accessLevel = 2 ;
}
2026-10-01 18:12:17 +03:00
message GroupAttributesAccessLevelChangeUpdate {
2026-04-13 16:32:10 +03:00
optional bytes updaterAci = 1 ;
2026-10-01 18:12:17 +03:00
GroupV2AccessLevel accessLevel = 2 ;
2026-04-13 16:32:10 +03:00
}
2025-01-18 02:58:43 +02:00
message GroupAnnouncementOnlyChangeUpdate {
optional bytes updaterAci = 1 ;
bool isAnnouncementOnly = 2 ;
}
message GroupAdminStatusUpdate {
optional bytes updaterAci = 1 ;
// The aci who had admin status granted or revoked.
bytes memberAci = 2 ;
bool wasAdminStatusGranted = 3 ;
}
message GroupMemberLeftUpdate {
bytes aci = 1 ;
}
message GroupMemberRemovedUpdate {
optional bytes removerAci = 1 ;
bytes removedAci = 2 ;
}
message SelfInvitedToGroupUpdate {
optional bytes inviterAci = 1 ;
}
message SelfInvitedOtherUserToGroupUpdate {
// If no invitee id available, use GroupUnknownInviteeUpdate
bytes inviteeServiceId = 1 ;
}
message GroupUnknownInviteeUpdate {
// Can be the self user.
optional bytes inviterAci = 1 ;
uint32 inviteeCount = 2 ;
}
message GroupInvitationAcceptedUpdate {
optional bytes inviterAci = 1 ;
bytes newMemberAci = 2 ;
}
message GroupInvitationDeclinedUpdate {
optional bytes inviterAci = 1 ;
// Note: if invited by pni, just set inviteeAci to nil.
optional bytes inviteeAci = 2 ;
}
message GroupMemberJoinedUpdate {
bytes newMemberAci = 1 ;
}
message GroupMemberAddedUpdate {
optional bytes updaterAci = 1 ;
bytes newMemberAci = 2 ;
bool hadOpenInvitation = 3 ;
// If hadOpenInvitation is true, optionally include aci of the inviter.
optional bytes inviterAci = 4 ;
}
// An invitation to self was revoked.
message GroupSelfInvitationRevokedUpdate {
optional bytes revokerAci = 1 ;
}
// These invitees should never be the local user.
// Use GroupSelfInvitationRevokedUpdate in those cases.
// The inviter or updater can be the local user.
message GroupInvitationRevokedUpdate {
message Invitee {
optional bytes inviterAci = 1 ;
// Prefer to use aci over pni. No need to set
// pni if aci is set. Both can be missing.
optional bytes inviteeAci = 2 ;
optional bytes inviteePni = 3 ;
}
// The member that revoked the invite(s), not the inviter!
// Assumed to be an admin (at the time, may no longer be an
// admin or even a member).
optional bytes updaterAci = 1 ;
repeated Invitee invitees = 2 ;
}
message GroupJoinRequestUpdate {
bytes requestorAci = 1 ;
}
message GroupJoinRequestApprovalUpdate {
bytes requestorAci = 1 ;
// The aci that approved or rejected the request.
optional bytes updaterAci = 2 ;
bool wasApproved = 3 ;
}
message GroupJoinRequestCanceledUpdate {
bytes requestorAci = 1 ;
}
// A single requestor has requested to join and cancelled
// their request repeatedly with no other updates in between.
// The last action encompassed by this update is always a
// cancellation; if there was another open request immediately
// after, it will be a separate GroupJoinRequestUpdate, either
// in the same frame or in a subsequent frame.
message GroupSequenceOfRequestsAndCancelsUpdate {
bytes requestorAci = 1 ;
uint32 count = 2 ;
}
message GroupInviteLinkResetUpdate {
optional bytes updaterAci = 1 ;
}
message GroupInviteLinkEnabledUpdate {
optional bytes updaterAci = 1 ;
bool linkRequiresAdminApproval = 2 ;
}
message GroupInviteLinkAdminApprovalUpdate {
optional bytes updaterAci = 1 ;
bool linkRequiresAdminApproval = 2 ;
}
message GroupInviteLinkDisabledUpdate {
optional bytes updaterAci = 1 ;
}
message GroupMemberJoinedByLinkUpdate {
bytes newMemberAci = 1 ;
}
// A gv1->gv2 migration occurred.
message GroupV2MigrationUpdate { }
// Another user migrated gv1->gv2 but was unable to add
// the local user and invited them instead.
message GroupV2MigrationSelfInvitedUpdate { }
// The local user migrated gv1->gv2 but was unable to
// add some members and invited them instead.
// (Happens if we don't have the invitee's profile key)
message GroupV2MigrationInvitedMembersUpdate {
uint32 invitedMembersCount = 1 ;
}
// The local user migrated gv1->gv2 but was unable to
// add or invite some members and dropped them instead.
// (Happens for e164 members where we don't have an aci).
message GroupV2MigrationDroppedMembersUpdate {
uint32 droppedMembersCount = 1 ;
}
// For 1:1 timer updates, use ExpirationTimerChatUpdate.
message GroupExpirationTimerUpdate {
uint64 expiresInMs = 1 ; // 0 means the expiration timer was disabled
optional bytes updaterAci = 2 ;
}
2026-10-01 18:12:17 +03:00
message GroupTerminateChangeUpdate {
optional bytes updaterAci = 1 ;
}
2025-10-29 14:10:25 +02:00
message PollTerminateUpdate {
uint64 targetSentTimestamp = 1 ;
string question = 2 ; // Between 1-100 characters
}
2026-02-09 14:13:18 +02:00
message PinMessageUpdate {
uint64 targetSentTimestamp = 1 ;
uint64 authorId = 2 ; // recipient id
}
2025-01-18 02:58:43 +02:00
message StickerPack {
bytes packId = 1 ;
bytes packKey = 2 ;
}
message ChatStyle {
message Gradient {
2026-10-01 18:12:17 +03:00
// Color ordering:
// 0 degrees: bottom-to-top
// 90 degrees: left-to-right
// 180 degrees: top-to-bottom
// 270 degrees: right-to-left
2025-01-18 02:58:43 +02:00
uint32 angle = 1 ; // degrees
repeated fixed32 colors = 2 ; // 0xAARRGGBB
repeated float positions = 3 ; // percent from 0 to 1
}
message CustomChatColor {
uint64 id = 1 ;
2025-01-30 12:41:05 +02:00
// If unset, use the default chat color
2025-01-18 02:58:43 +02:00
oneof color {
fixed32 solid = 2 ; // 0xAARRGGBB
Gradient gradient = 3 ;
}
}
message AutomaticBubbleColor {
}
enum WallpaperPreset {
2025-01-30 12:41:05 +02:00
UNKNOWN_WALLPAPER_PRESET = 0 ; // Interpret as the wallpaper being unset
2025-01-18 02:58:43 +02:00
SOLID_BLUSH = 1 ;
SOLID_COPPER = 2 ;
SOLID_DUST = 3 ;
SOLID_CELADON = 4 ;
SOLID_RAINFOREST = 5 ;
SOLID_PACIFIC = 6 ;
SOLID_FROST = 7 ;
SOLID_NAVY = 8 ;
SOLID_LILAC = 9 ;
SOLID_PINK = 10 ;
SOLID_EGGPLANT = 11 ;
SOLID_SILVER = 12 ;
GRADIENT_SUNSET = 13 ;
GRADIENT_NOIR = 14 ;
GRADIENT_HEATMAP = 15 ;
GRADIENT_AQUA = 16 ;
GRADIENT_IRIDESCENT = 17 ;
GRADIENT_MONSTERA = 18 ;
GRADIENT_BLISS = 19 ;
GRADIENT_SKY = 20 ;
GRADIENT_PEACH = 21 ;
}
enum BubbleColorPreset {
2025-01-30 12:41:05 +02:00
UNKNOWN_BUBBLE_COLOR_PRESET = 0 ; // Interpret as the user's default chat bubble color
2025-01-18 02:58:43 +02:00
SOLID_ULTRAMARINE = 1 ;
SOLID_CRIMSON = 2 ;
SOLID_VERMILION = 3 ;
SOLID_BURLAP = 4 ;
SOLID_FOREST = 5 ;
SOLID_WINTERGREEN = 6 ;
SOLID_TEAL = 7 ;
SOLID_BLUE = 8 ;
SOLID_INDIGO = 9 ;
SOLID_VIOLET = 10 ;
SOLID_PLUM = 11 ;
SOLID_TAUPE = 12 ;
SOLID_STEEL = 13 ;
GRADIENT_EMBER = 14 ;
GRADIENT_MIDNIGHT = 15 ;
GRADIENT_INFRARED = 16 ;
GRADIENT_LAGOON = 17 ;
GRADIENT_FLUORESCENT = 18 ;
GRADIENT_BASIL = 19 ;
GRADIENT_SUBLIME = 20 ;
GRADIENT_SEA = 21 ;
GRADIENT_TANGERINE = 22 ;
}
2025-01-30 12:41:05 +02:00
// If unset, importers should consider there to be no wallpaper.
2025-01-18 02:58:43 +02:00
oneof wallpaper {
WallpaperPreset wallpaperPreset = 1 ;
// This `FilePointer` is expected not to contain a `fileName`, `width`,
// `height`, or `caption`.
FilePointer wallpaperPhoto = 2 ;
}
2025-01-30 12:41:05 +02:00
// If unset, importers should consider it to be AutomaticBubbleColor
2025-01-18 02:58:43 +02:00
oneof bubbleColor {
// Bubble setting is automatically determined based on the wallpaper setting,
// or `SOLID_ULTRAMARINE` for `noWallpaper`
AutomaticBubbleColor autoBubbleColor = 3 ;
BubbleColorPreset bubbleColorPreset = 4 ;
// See AccountSettings.customChatColors
uint64 customColorId = 5 ;
}
bool dimWallpaperInDarkMode = 7 ;
}
message NotificationProfile {
enum DayOfWeek {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Monday"
2025-01-18 02:58:43 +02:00
MONDAY = 1 ;
TUESDAY = 2 ;
WEDNESDAY = 3 ;
THURSDAY = 4 ;
FRIDAY = 5 ;
SATURDAY = 6 ;
SUNDAY = 7 ;
}
string name = 1 ;
optional string emoji = 2 ;
fixed32 color = 3 ; // 0xAARRGGBB
uint64 createdAtMs = 4 ;
bool allowAllCalls = 5 ;
bool allowAllMentions = 6 ;
repeated uint64 allowedMembers = 7 ; // generated recipient id for allowed groups and contacts
bool scheduleEnabled = 8 ;
uint32 scheduleStartTime = 9 ; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
uint32 scheduleEndTime = 10 ; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
repeated DayOfWeek scheduleDaysEnabled = 11 ;
2025-06-03 20:26:10 +03:00
bytes id = 12 ; // should be 16 bytes
2025-01-18 02:58:43 +02:00
}
message ChatFolder {
// Represents the default "All chats" folder record vs all other custom folders
enum FolderType {
2025-01-30 12:41:05 +02:00
UNKNOWN = 0 ; // Interpret as "Custom"
2025-01-18 02:58:43 +02:00
ALL = 1 ;
CUSTOM = 2 ;
}
string name = 1 ;
bool showOnlyUnread = 2 ;
bool showMutedChats = 3 ;
// Folder includes all 1:1 chats, unless excluded
bool includeAllIndividualChats = 4 ;
// Folder includes all group chats, unless excluded
bool includeAllGroupChats = 5 ;
FolderType folderType = 6 ;
repeated uint64 includedRecipientIds = 7 ; // generated recipient id of groups, contacts, and/or note to self
repeated uint64 excludedRecipientIds = 8 ; // generated recipient id of groups, contacts, and/or note to self
2025-04-15 15:22:31 +03:00
bytes id = 9 ; // should be 16 bytes
2025-01-18 02:58:43 +02:00
}