syntax = "proto3"; package chatto.api.v1; import "buf/validate/validate.proto"; import "chatto/api/v1/pagination.proto"; import "chatto/api/v1/rooms.proto"; import "chatto/api/v1/users.proto"; import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; // Delivery behavior for one notification signal class. enum NotificationDeliveryMode { option allow_alias = true; // No delivery mode was specified. This value is not a valid override. NOTIFICATION_DELIVERY_MODE_UNSPECIFIED = 0; // Matching activity does not create user attention. NOTIFICATION_DELIVERY_MODE_OFF = 1; // Deprecated alias for NOTIFICATION_DELIVERY_MODE_IN_APP_NOTIFICATION. NOTIFICATION_DELIVERY_MODE_SILENT = 2 [deprecated = true]; // Matching activity creates an in-app notification but does not send push. NOTIFICATION_DELIVERY_MODE_IN_APP_NOTIFICATION = 2; // Deprecated alias for NOTIFICATION_DELIVERY_MODE_PUSH_NOTIFICATION. NOTIFICATION_DELIVERY_MODE_ALERT = 3 [deprecated = true]; // Matching activity creates an in-app notification and may send push. NOTIFICATION_DELIVERY_MODE_PUSH_NOTIFICATION = 3; // Matching activity adds a neutral unread indicator without creating a // notification occurrence, playing a sound, or sending push. NOTIFICATION_DELIVERY_MODE_UNREAD_BADGE = 4; } // Delivery modes for Chatto's finite built-in notification signal classes. // Presence is significant: absent fields inherit in a stored policy. message NotificationDeliveryModes { optional NotificationDeliveryMode direct_messages = 1; optional NotificationDeliveryMode direct_mentions = 2; optional NotificationDeliveryMode replies = 3; optional NotificationDeliveryMode role_mentions = 4; optional NotificationDeliveryMode here_mentions = 5; optional NotificationDeliveryMode all_mentions = 6; optional NotificationDeliveryMode followed_threads = 7; // Deprecated compatibility slot. Use room_messages at room scope. optional NotificationDeliveryMode followed_rooms = 8 [deprecated = true]; optional NotificationDeliveryMode reactions = 9; // Delivery mode for root messages posted directly to joined channel rooms. // Thread messages and direct messages use their own signal classes. optional NotificationDeliveryMode room_messages = 10; } // Visual importance of an unread notification. This is independent from // whether the notification is eligible for sound or push delivery. enum NotificationAttentionLevel { // No attention level was provided. NOTIFICATION_ATTENTION_LEVEL_UNSPECIFIED = 0; // Ambient activity appears with a neutral unread indicator. NOTIFICATION_ATTENTION_LEVEL_AMBIENT = 1; // Important activity appears with Chatto's notification orange. NOTIFICATION_ATTENTION_LEVEL_IMPORTANT = 2; } // Exact visible message destination carried by a notification signal. message NotificationMessageReference { // Room containing the source activity. RoomSummary room = 1; // Exact source or reacted-to message event to reveal. string event_id = 2; // Thread root when the target is inside a thread. optional string thread_root_event_id = 3; } // A direct message sent to the viewer. message DirectMessageReceived { NotificationMessageReference message = 1; } // A message that mentions the viewer directly. message DirectMentionReceived { NotificationMessageReference message = 1; } // A reply to one of the viewer's messages. message ReplyReceived { NotificationMessageReference message = 1; } // A message mentioning a role held by the viewer. message RoleMentionReceived { NotificationMessageReference message = 1; // Source-time role handles that selected the viewer. Multiple matching role // mentions are consolidated into one occurrence for the source message. repeated string role_names = 2; } // An `@here` message that included the viewer. message HereMentionReceived { NotificationMessageReference message = 1; } // An `@all` message that included the viewer. message AllMentionReceived { NotificationMessageReference message = 1; } // Activity in a thread followed by the viewer. message FollowedThreadActivity { NotificationMessageReference message = 1; } // Deprecated compatibility payload. Chatto does not derive this signal. message FollowedRoomActivity { option deprecated = true; NotificationMessageReference message = 1; } // A root message posted directly to a channel room that the viewer belongs to. message RoomMessageReceived { NotificationMessageReference message = 1; } // A reaction added to one of the viewer's messages. message ReactionReceived { NotificationMessageReference message = 1; // Exact reaction supplied by the actor. string emoji = 2; } // Immutable, typed cause and destination of one notification. Clients must // preserve unknown variants as generic dismissible rows and must not infer // navigation semantics for them. message NotificationSignal { oneof kind { DirectMessageReceived direct_message_received = 1; DirectMentionReceived direct_mention_received = 2; ReplyReceived reply_received = 3; RoleMentionReceived role_mention_received = 4; HereMentionReceived here_mention_received = 5; AllMentionReceived all_mention_received = 6; FollowedThreadActivity followed_thread_activity = 7; FollowedRoomActivity followed_room_activity = 8 [deprecated = true]; ReactionReceived reaction_received = 9; RoomMessageReceived room_message_received = 10; } } // One exact Notifications 2.0 source occurrence. message NotificationOccurrence { // Stable occurrence ID. string id = 1; // Time of the source activity. google.protobuf.Timestamp created_at = 2; // User who caused the source activity, when still visible. User actor = 3; // Immutable cause and destination represented by this occurrence. NotificationSignal signal = 4; // Whether this occurrence still contributes unread attention. bool unread = 5; // Absolute expiry, 90 days after the source activity. google.protobuf.Timestamp expires_at = 6; // Source-time visual importance of this occurrence. NotificationAttentionLevel attention_level = 7; } // Request for one page of notification occurrences. message ListNotificationOccurrencesRequest { // Page request. Defaults to 50 results when absent or limit is zero. PageRequest page = 1; } // One page of exact notification occurrences. message ListNotificationOccurrencesResponse { // Occurrences newest activity first. repeated NotificationOccurrence occurrences = 1; // Page metadata. PageInfo page = 2; // Total unread occurrence count, independent of presentation grouping. int32 unread_count = 3; // Earliest expiry in the complete list, including occurrences outside this page. // Clients refresh authoritative notification state at this boundary. google.protobuf.Timestamp next_expiry_at = 4; // Complete unread-occurrence counts by target room. This is independent of // the bounded page so room indicators remain authoritative. repeated NotificationRoomUnreadCount room_unread_counts = 5; // Exact unread occurrences with Important attention. int32 important_unread_count = 6; } // Request one exact notification occurrence by stable ID. message GetNotificationOccurrenceRequest { // Required stable occurrence ID. string notification_id = 1 [(buf.validate.field).string.min_len = 1]; } // One exact notification occurrence. message GetNotificationOccurrenceResponse { // Visible occurrence. Absence is reported as NOT_FOUND. NotificationOccurrence occurrence = 1; } // Request an explicit bounded set of notification occurrences. message BatchGetNotificationOccurrencesRequest { // Stable occurrence IDs. Unknown, deleted, and currently invisible IDs are // omitted. Repeated IDs are de-duplicated in first-seen request order. repeated string notification_ids = 1 [(buf.validate.field).repeated = { min_items: 1 max_items: 100 items: { string: {min_len: 1} } }]; } // Exact visible notification occurrences requested by stable ID. message BatchGetNotificationOccurrencesResponse { // Found occurrences in first-seen request order. repeated NotificationOccurrence occurrences = 1; } // Unread notification occurrences currently targeting one room. message NotificationRoomUnreadCount { // Target room ID. string room_id = 1; // Number of unread occurrences targeting this room. int32 unread_count = 2; // Unread occurrences in this room with Important attention. int32 important_unread_count = 3; } // Request to mark one notification occurrence read. message MarkNotificationReadRequest { // Required stable occurrence ID. string notification_id = 1 [(buf.validate.field).string.min_len = 1]; } // Notification occurrence after marking it read. message MarkNotificationReadResponse { // Updated occurrence. NotificationOccurrence occurrence = 1; } // Request permanent deletion of one notification occurrence. message DeleteNotificationOccurrenceRequest { // Required stable occurrence ID. string notification_id = 1 [(buf.validate.field).string.min_len = 1]; } // Result of deleting one notification occurrence. message DeleteNotificationOccurrenceResponse { // True when this call replaced a visible occurrence with a deletion tombstone. // False when the occurrence was already absent or deleted. bool deleted = 1; } // Request permanent deletion of an explicit bounded set of occurrences. message BatchDeleteNotificationOccurrencesRequest { // Stable occurrence IDs to delete. Duplicates and already absent IDs are // ignored. The server accepts at most 100 IDs. repeated string notification_ids = 1 [(buf.validate.field).repeated = { min_items: 1 max_items: 100 items: { string: {min_len: 1} } }]; } // Result of deleting exact notification occurrences. message BatchDeleteNotificationOccurrencesResponse { // Number of visible occurrences replaced by deletion tombstones. int32 deleted_count = 1; } // Request permanent deletion of every currently visible notification occurrence. message DeleteAllNotificationOccurrencesRequest {} // Result of deleting every currently visible notification occurrence. message DeleteAllNotificationOccurrencesResponse { // Number of visible occurrences replaced by deletion tombstones. int32 deleted_count = 1; } // Explicit and effective delivery modes for one notification policy scope. message NotificationPolicy { // Explicit modes at the requested scope. Missing fields mean Inherit. NotificationDeliveryModes overrides = 1; // Complete modes after applying product, server, and room inheritance. NotificationDeliveryModes effective = 2; } // Request the authenticated viewer's notification policy. message GetNotificationPolicyRequest { // Empty returns server-scoped preferences. A room ID returns the inherited // effective policy for that room and requires current membership. optional string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // Complete supported notification policy for one scope. message GetNotificationPolicyResponse { // Room scope when requested; absent for server scope. optional string room_id = 1; // Explicit and effective policy for the requested scope. NotificationPolicy policy = 2; } // Sparsely update notification policy overrides. message UpdateNotificationPolicyRequest { // Room scope to change; absent changes the server scope. optional string room_id = 1 [(buf.validate.field).string.min_len = 1]; // Sparse override values. Fields selected by update_mask but absent here are // cleared to Inherit. NotificationDeliveryModes overrides = 2; // Fields to set or clear, relative to overrides. The mask must contain at // least one supported delivery-mode field. google.protobuf.FieldMask update_mask = 3; } // Complete supported notification policy after a sparse update. message UpdateNotificationPolicyResponse { // Room scope when changed; absent for server scope. optional string room_id = 1; // Explicit and effective policy after the update. NotificationPolicy policy = 2; } // Explicit target for one notification policy. Clients must select exactly // one scope. Direct-message rooms use room scope and never inherit from a room // group. message NotificationPolicyScope { oneof scope { option (buf.validate.oneof).required = true; // Selects the authenticated viewer's server-wide policy. google.protobuf.Empty server = 1; // Selects one existing room group's policy. string room_group_id = 2 [(buf.validate.field).string.min_len = 1]; // Selects one room's policy and requires current room membership. string room_id = 3 [(buf.validate.field).string.min_len = 1]; } } // One policy together with the explicit scope that produced it. message ScopedNotificationPolicy { // Exact policy scope. NotificationPolicyScope scope = 1; // Explicit and effective modes for the scope. NotificationPolicy policy = 2; } // Request one notification policy for the authenticated viewer. message NotificationPolicyServiceGetNotificationPolicyRequest { // Required policy scope. NotificationPolicyScope scope = 1 [(buf.validate.field).required = true]; } // One resolved notification policy. message NotificationPolicyServiceGetNotificationPolicyResponse { // Requested policy and its explicit scope. ScopedNotificationPolicy policy = 1; } // Request an explicit bounded set of notification policies. message BatchGetNotificationPoliciesRequest { // Required scopes. Missing or inaccessible scopes are omitted. Repeated // scopes are de-duplicated in first-seen request order. repeated NotificationPolicyScope scopes = 1 [(buf.validate.field).repeated = { min_items: 1 max_items: 100 }]; } // Resolved notification policies requested by scope. message BatchGetNotificationPoliciesResponse { // Found policies in first-seen request order. repeated ScopedNotificationPolicy policies = 1; } // Sparsely update notification policy overrides at one explicit scope. message NotificationPolicyServiceUpdateNotificationPolicyRequest { // Required policy scope. NotificationPolicyScope scope = 1 [(buf.validate.field).required = true]; // Sparse override values. Fields selected by update_mask but absent here are // cleared to Inherit. NotificationDeliveryModes overrides = 2; // Fields to set or clear, relative to overrides. The mask must contain at // least one supported delivery-mode field. google.protobuf.FieldMask update_mask = 3; } // Complete supported notification policy after a scoped sparse update. message NotificationPolicyServiceUpdateNotificationPolicyResponse { // Updated policy and its explicit scope. ScopedNotificationPolicy policy = 1; } // Reads, marks read, and deletes notifications for the authenticated viewer. service NotificationService { // Gets one exact visible occurrence. Message-derived occurrences require // current room membership. Channel-room occurrences also require message.read // or a matching thread relationship with message.read-interactions. DM // membership authorizes DM occurrences. Returns NOT_FOUND when the occurrence // is absent, deleted, expired, or no longer visible to the authenticated // viewer. Returns UNIMPLEMENTED when this server cannot validate its signal // kind. rpc GetNotificationOccurrence(GetNotificationOccurrenceRequest) returns (GetNotificationOccurrenceResponse); // Gets an explicit bounded set of visible occurrences. Missing or // inaccessible IDs are omitted and duplicate IDs are de-duplicated. Returns // UNIMPLEMENTED rather than partially answering when this server cannot // validate one requested signal kind. rpc BatchGetNotificationOccurrences(BatchGetNotificationOccurrencesRequest) returns (BatchGetNotificationOccurrencesResponse); // Lists exact Notifications 2.0 occurrences. Clients may derive temporary // presentation groups without changing occurrence identity or counts. // Message-derived occurrences require current room membership. Channel-room // occurrences also require message.read or a matching thread relationship // with message.read-interactions. DM membership authorizes DM occurrences. // Returns UNIMPLEMENTED rather than silently omitting an occurrence whose // signal kind this server version cannot validate and assemble. rpc ListNotificationOccurrences(ListNotificationOccurrencesRequest) returns (ListNotificationOccurrencesResponse); // Marks one occurrence read. Repeating the call is safe. Returns // UNIMPLEMENTED when this server cannot validate the occurrence signal kind. rpc MarkNotificationRead(MarkNotificationReadRequest) returns (MarkNotificationReadResponse) { option idempotency_level = IDEMPOTENT; } // Permanently deletes one occurrence while retaining its anti-recreation // tombstone through the original expiry. Repeating the call is safe. Returns // UNIMPLEMENTED when this server cannot validate the occurrence signal kind. rpc DeleteNotificationOccurrence(DeleteNotificationOccurrenceRequest) returns (DeleteNotificationOccurrenceResponse) { option idempotency_level = IDEMPOTENT; } // Permanently deletes an exact set of occurrences. Repeating the call is // safe because later activity receives different occurrence IDs. Returns // UNIMPLEMENTED rather than partially deleting a signal kind this server // cannot validate. rpc BatchDeleteNotificationOccurrences(BatchDeleteNotificationOccurrencesRequest) returns (BatchDeleteNotificationOccurrencesResponse) { option idempotency_level = IDEMPOTENT; } // Permanently deletes every visible occurrence current at the server's // authoritative mutation boundary. Callers must not retry this mutation // automatically because later activity may arrive after that boundary. // Returns UNIMPLEMENTED rather than partially deleting a signal kind this // server cannot validate. rpc DeleteAllNotificationOccurrences(DeleteAllNotificationOccurrencesRequest) returns (DeleteAllNotificationOccurrencesResponse); // Gets explicit and effective modes for every supported signal class. rpc GetNotificationPolicy(GetNotificationPolicyRequest) returns (GetNotificationPolicyResponse); // Atomically sets or clears selected server- or room-scoped signal-class overrides. rpc UpdateNotificationPolicy(UpdateNotificationPolicyRequest) returns (UpdateNotificationPolicyResponse) { option idempotency_level = IDEMPOTENT; } } // Reads and updates the authenticated viewer's server, room-group, and room // notification delivery policies. service NotificationPolicyService { // Gets explicit and effective modes for one policy scope. Returns NOT_FOUND // for a missing room group or room, and PERMISSION_DENIED when the viewer is // not a current member of a requested room. rpc GetNotificationPolicy(NotificationPolicyServiceGetNotificationPolicyRequest) returns (NotificationPolicyServiceGetNotificationPolicyResponse); // Gets an explicit bounded set of policies. Missing and inaccessible scopes // are omitted, and duplicate scopes are de-duplicated in first-seen order. rpc BatchGetNotificationPolicies(BatchGetNotificationPoliciesRequest) returns (BatchGetNotificationPoliciesResponse); // Atomically sets or clears selected overrides at one explicit scope. rpc UpdateNotificationPolicy(NotificationPolicyServiceUpdateNotificationPolicyRequest) returns (NotificationPolicyServiceUpdateNotificationPolicyResponse) { option idempotency_level = IDEMPOTENT; } }