syntax = "proto3"; package memos.api.v1; import "api/v1/attachment_service.proto"; import "api/v1/common.proto"; import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; option go_package = "gen/api/v1"; service MemoService { // CreateMemo creates a memo. The request body is a Memo; set its content // (Markdown) and visibility (PRIVATE | PROTECTED | PUBLIC | SPACE, // default PRIVATE). // The memo is owned by the authenticated user; requires authentication. rpc CreateMemo(CreateMemoRequest) returns (Memo) { option (google.api.http) = { post: "/api/v1/memos" body: "memo" }; option (google.api.method_signature) = "memo"; } // ListMemos lists readable non-comment memos with pagination and filter. rpc ListMemos(ListMemosRequest) returns (ListMemosResponse) { option (google.api.http) = {get: "/api/v1/memos"}; option (google.api.method_signature) = ""; } // GetMemo gets a memo. rpc GetMemo(GetMemoRequest) returns (Memo) { option (google.api.http) = {get: "/api/v1/{name=memos/*}"}; option (google.api.method_signature) = "name"; } // UpdateMemo updates a memo. rpc UpdateMemo(UpdateMemoRequest) returns (Memo) { option (google.api.http) = { patch: "/api/v1/{memo.name=memos/*}" body: "memo" }; option (google.api.method_signature) = "memo,update_mask"; } // DeleteMemo deletes only the named memo and its owned resources. It removes // incident relations but never deletes another memo. rpc DeleteMemo(DeleteMemoRequest) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/api/v1/{name=memos/*}"}; option (google.api.method_signature) = "name"; } // SetMemoAttachments replaces the full set of attachments on a memo with the // provided list (not an append). Pass the complete desired set; an empty list // clears all attachments. Idempotent. rpc SetMemoAttachments(SetMemoAttachmentsRequest) returns (google.protobuf.Empty) { option (google.api.http) = { patch: "/api/v1/{name=memos/*}/attachments" body: "*" }; option (google.api.method_signature) = "name"; } // ListMemoAttachments lists attachments for a memo. rpc ListMemoAttachments(ListMemoAttachmentsRequest) returns (ListMemoAttachmentsResponse) { option (google.api.http) = {get: "/api/v1/{name=memos/*}/attachments"}; option (google.api.method_signature) = "name"; } // SetMemoRelations replaces the full set of mutable REFERENCE relations on a // memo. COMMENT relations are immutable creation context and are rejected by // this RPC. An empty list clears references without changing COMMENT. rpc SetMemoRelations(SetMemoRelationsRequest) returns (google.protobuf.Empty) { option (google.api.http) = { patch: "/api/v1/{name=memos/*}/relations" body: "*" }; option (google.api.method_signature) = "name"; } // ListMemoRelations lists relations for a memo. rpc ListMemoRelations(ListMemoRelationsRequest) returns (ListMemoRelationsResponse) { option (google.api.http) = {get: "/api/v1/{name=memos/*}/relations"}; option (google.api.method_signature) = "name"; } // CreateMemoComment atomically creates an independent memo plus one immutable // COMMENT context relation to the named memo. The new memo keeps its own // placement, visibility, authorship, and lifecycle. rpc CreateMemoComment(CreateMemoCommentRequest) returns (Memo) { option (google.api.http) = { post: "/api/v1/{name=memos/*}/comments" body: "comment" }; option (google.api.method_signature) = "name,comment"; } // ListMemoComments lists comments for a memo. rpc ListMemoComments(ListMemoCommentsRequest) returns (ListMemoCommentsResponse) { option (google.api.http) = {get: "/api/v1/{name=memos/*}/comments"}; option (google.api.method_signature) = "name"; } // ListMemoReactions lists reactions for a memo. rpc ListMemoReactions(ListMemoReactionsRequest) returns (ListMemoReactionsResponse) { option (google.api.http) = {get: "/api/v1/{name=memos/*}/reactions"}; option (google.api.method_signature) = "name"; } // UpsertMemoReaction adds or updates the authenticated user's reaction on a memo. rpc UpsertMemoReaction(UpsertMemoReactionRequest) returns (Reaction) { option (google.api.http) = { post: "/api/v1/{name=memos/*}/reactions" body: "*" }; option (google.api.method_signature) = "name"; } // DeleteMemoReaction deletes a reaction for a memo. rpc DeleteMemoReaction(DeleteMemoReactionRequest) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/api/v1/{name=memos/*/reactions/*}"}; option (google.api.method_signature) = "name"; } // CreateMemoShare creates a share link for a memo. Requires authentication as the memo creator. rpc CreateMemoShare(CreateMemoShareRequest) returns (MemoShare) { option (google.api.http) = { post: "/api/v1/{parent=memos/*}/shares" body: "memo_share" }; option (google.api.method_signature) = "parent,memo_share"; } // ListMemoShares lists all share links for a memo. Requires authentication as the memo creator. rpc ListMemoShares(ListMemoSharesRequest) returns (ListMemoSharesResponse) { option (google.api.http) = {get: "/api/v1/{parent=memos/*}/shares"}; option (google.api.method_signature) = "parent"; } // DeleteMemoShare revokes a share link. Requires authentication as the memo creator. rpc DeleteMemoShare(DeleteMemoShareRequest) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/api/v1/{name=memos/*/shares/*}"}; option (google.api.method_signature) = "name"; } // GetSharedMemo resolves a share token to its memo. No authentication required. // Returns NOT_FOUND if the token is invalid or expired. rpc GetSharedMemo(GetSharedMemoRequest) returns (Memo) { option (google.api.http) = {get: "/api/v1/shares/{share_token}/memo"}; } // GetLinkMetadata gets metadata for a link. rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) { option (google.api.http) = {get: "/api/v1/memos/-/linkMetadata"}; } // BatchGetLinkMetadata gets metadata for links. rpc BatchGetLinkMetadata(BatchGetLinkMetadataRequest) returns (BatchGetLinkMetadataResponse) { option (google.api.http) = { post: "/api/v1/memos/-/linkMetadata:batchGet" body: "*" }; } } // Visibility controls who can read a memo. enum Visibility { VISIBILITY_UNSPECIFIED = 0; // PRIVATE: only the creator can read the memo. PRIVATE = 1; // PROTECTED: signed-in users of the instance can read the memo. PROTECTED = 2; // PUBLIC: signed-in users can read the memo; anonymous visitors can read it // when instance policy permits. PUBLIC = 3; // SPACE: active members of the memo's space can read it. SPACE = 4; } // Reaction is a reaction attached to a memo. message Reaction { option (google.api.resource) = { type: "memos.api.v1/Reaction" pattern: "memos/{memo}/reactions/{reaction}" name_field: "name" singular: "reaction" plural: "reactions" }; // The resource name of the reaction. // Format: memos/{memo}/reactions/{reaction} string name = 1 [ (google.api.field_behavior) = OUTPUT_ONLY, (google.api.field_behavior) = IDENTIFIER ]; // The resource name of the creator. // Format: users/{user} string creator = 2 [ (google.api.field_behavior) = OUTPUT_ONLY, (google.api.resource_reference) = {type: "memos.api.v1/User"} ]; reserved 3; reserved "content_id"; // Required. The type of reaction (e.g., "👍", "❤️", "😄"). string reaction_type = 4 [(google.api.field_behavior) = REQUIRED]; // Output only. The creation timestamp. google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; } message Memo { option (google.api.resource) = { type: "memos.api.v1/Memo" pattern: "memos/{memo}" name_field: "name" singular: "memo" plural: "memos" }; // The resource name of the memo. // Format: memos/{memo}, where memo is the user-defined UID. string name = 1 [(google.api.field_behavior) = IDENTIFIER]; // The state of the memo. State state = 2 [(google.api.field_behavior) = REQUIRED]; // The name of the creator. // Format: users/{user} string creator = 3 [ (google.api.field_behavior) = OUTPUT_ONLY, (google.api.resource_reference) = {type: "memos.api.v1/User"} ]; // The creation timestamp. // If not set on creation, the server will set it to the current time. google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OPTIONAL]; // The last update timestamp. // If not set on creation, the server will set it to the current time. google.protobuf.Timestamp update_time = 5 [(google.api.field_behavior) = OPTIONAL]; reserved 6; reserved "display_time"; // Required. The content of the memo in Markdown format. string content = 7 [(google.api.field_behavior) = REQUIRED]; // The visibility of the memo. // One of PRIVATE (creator only), PROTECTED (signed-in users), PUBLIC // (anonymous-eligible), or SPACE (active space members). Defaults to // PRIVATE on creation when unspecified. Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED]; // Output only. The tags extracted from the content. repeated string tags = 10 [(google.api.field_behavior) = OUTPUT_ONLY]; // Whether the memo is pinned. bool pinned = 11 [(google.api.field_behavior) = OPTIONAL]; // Optional. The attachments of the memo. repeated Attachment attachments = 12 [(google.api.field_behavior) = OPTIONAL]; // Optional. The relations of the memo. repeated MemoRelation relations = 13 [(google.api.field_behavior) = OPTIONAL]; // Output only. The reactions to the memo. repeated Reaction reactions = 14 [(google.api.field_behavior) = OUTPUT_ONLY]; // Output only. The computed properties of the memo. Property property = 15 [(google.api.field_behavior) = OUTPUT_ONLY]; // Output only. The context memo of this COMMENT relation, if any. // Its identity is returned even when the caller cannot read the parent. // Fetch the parent independently; this field does not grant read access. // Format: memos/{memo} optional string parent = 16 [ (google.api.field_behavior) = OUTPUT_ONLY, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Output only. The snippet of the memo content. Plain text only. string snippet = 17 [(google.api.field_behavior) = OUTPUT_ONLY]; // Optional. The location of the memo. optional Location location = 18 [(google.api.field_behavior) = OPTIONAL]; // Optional. The space in which this memo is placed. Format: spaces/{space}. // Every memo, including a comment, owns its placement independently. optional string space = 19 [ (google.api.field_behavior) = OPTIONAL, (google.api.resource_reference) = {type: "memos.api.v1/Space"} ]; // Computed properties of a memo. message Property { bool has_link = 1; bool has_task_list = 2; bool has_code = 3; bool has_incomplete_tasks = 4; // The title extracted from the first H1 heading, if present. string title = 5; } } message Location { // A placeholder text for the location. string placeholder = 1 [(google.api.field_behavior) = OPTIONAL]; // The latitude of the location. double latitude = 2 [(google.api.field_behavior) = OPTIONAL]; // The longitude of the location. double longitude = 3 [(google.api.field_behavior) = OPTIONAL]; } message CreateMemoRequest { // Required. The memo to create. Memo memo = 1 [(google.api.field_behavior) = REQUIRED]; // Optional. The memo ID to use for this memo. // If empty, a unique ID will be generated. // Format: ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?$ string memo_id = 2 [(google.api.field_behavior) = OPTIONAL]; } message ListMemosRequest { // Optional. The maximum number of memos to return. // The service may return fewer than this value. // If unspecified, at most 50 memos will be returned. // The maximum value is 1000; values above 1000 will be coerced to 1000. int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token, received from a previous `ListMemos` call. // Provide this to retrieve the subsequent page. string page_token = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. The state of the memos to list. // Default to `NORMAL`. Set to `ARCHIVED` to list archived memos. State state = 3 [(google.api.field_behavior) = OPTIONAL]; // Optional. The order to sort results by. // Default to "create_time desc". // Supports comma-separated list of fields following AIP-132. // Example: "pinned desc, create_time desc" or "update_time asc" // Supported fields: pinned, create_time, update_time, name. // Note: order_by uses create_time / update_time, while the filter // expression uses created_ts / updated_ts for the same timestamps. string order_by = 4 [(google.api.field_behavior) = OPTIONAL]; // Optional. A CEL expression to filter memos. Combine terms with && and ||. // Available fields: // content (string), creator (string, e.g. "users/1"), // created_ts / updated_ts (timestamp), pinned (bool), // visibility (string: PRIVATE | PROTECTED | PUBLIC | SPACE), // space (string resource name, or null when the memo has no space; // supports == and comparisons against null, e.g. space != null), // tags (list; match with `"work" in tags`, not `tag == "work"`), // has_task_list / has_link / has_code / has_incomplete_tasks (bool), // has_location (bool; true when the memo has a location attached). // Note: the time fields here are created_ts / updated_ts, which differ from // the create_time / update_time names used by order_by. // Examples: // pinned == true && visibility == "PUBLIC" // space == "spaces/team" or space == null // tags.exists(t, t == "urgent") // content.contains("roadmap") && created_ts > now - duration("168h") string filter = 5 [(google.api.field_behavior) = OPTIONAL]; // Optional. If true, show deleted memos in the response. bool show_deleted = 6 [(google.api.field_behavior) = OPTIONAL]; reserved 7, 8; reserved "space", "unassigned"; } message ListMemosResponse { // The list of memos. repeated Memo memos = 1; // A token that can be sent as `page_token` to retrieve the next page. // If this field is omitted, there are no subsequent pages. string next_page_token = 2; } message GetMemoRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; } message UpdateMemoRequest { // Required. The memo to update. // The `name` field is required. Memo memo = 1 [(google.api.field_behavior) = REQUIRED]; // Required. The list of fields to update. google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; } message DeleteMemoRequest { // Required. The resource name of the memo to delete. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Optional. If set to true, the memo will be deleted even if it has associated data. bool force = 2 [(google.api.field_behavior) = OPTIONAL]; } message SetMemoAttachmentsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Required. The attachments to set for the memo. repeated Attachment attachments = 2 [(google.api.field_behavior) = REQUIRED]; } message ListMemoAttachmentsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Optional. The maximum number of attachments to return. int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token for pagination. string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListMemoAttachmentsResponse { // The list of attachments. repeated Attachment attachments = 1; // A token for the next page of results. string next_page_token = 2; } message MemoRelation { // The memo in the relation. Memo memo = 1 [(google.api.field_behavior) = REQUIRED]; // The related memo. Memo related_memo = 2 [(google.api.field_behavior) = REQUIRED]; // The type of the relation. enum Type { TYPE_UNSPECIFIED = 0; REFERENCE = 1; COMMENT = 2; } Type type = 3 [(google.api.field_behavior) = REQUIRED]; // Memo reference in relations. message Memo { // The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Output only. The snippet of the memo content. Plain text only. string snippet = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; } } message SetMemoRelationsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Required. The REFERENCE relations to set for the memo. COMMENT is invalid. repeated MemoRelation relations = 2 [(google.api.field_behavior) = REQUIRED]; } message ListMemoRelationsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Optional. The maximum number of relations to return. int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token for pagination. string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListMemoRelationsResponse { // The list of relations. repeated MemoRelation relations = 1; // A token for the next page of results. string next_page_token = 2; } message CreateMemoCommentRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Required. The comment to create. Memo comment = 2 [(google.api.field_behavior) = REQUIRED]; // Optional. The comment ID to use. string comment_id = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListMemoCommentsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Optional. The maximum number of comments to return. int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token for pagination. string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; // Optional. The order to sort results by. string order_by = 4 [(google.api.field_behavior) = OPTIONAL]; } message ListMemoCommentsResponse { // The list of comment memos. repeated Memo memos = 1; // A token for the next page of results. string next_page_token = 2; } message ListMemoReactionsRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Optional. The maximum number of reactions to return. int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token for pagination. string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; } message ListMemoReactionsResponse { // The list of reactions. repeated Reaction reactions = 1; // A token for the next page of results. string next_page_token = 2; } message UpsertMemoReactionRequest { // Required. The resource name of the memo. // Format: memos/{memo} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Required. The reaction to upsert. Reaction reaction = 2 [(google.api.field_behavior) = REQUIRED]; } message DeleteMemoReactionRequest { // Required. The resource name of the reaction to delete. // Format: memos/{memo}/reactions/{reaction} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Reaction"} ]; } // MemoShare is an access grant that permits read-only access to a memo via an opaque bearer token. message MemoShare { option (google.api.resource) = { type: "memos.api.v1/MemoShare" pattern: "memos/{memo}/shares/{share}" singular: "share" plural: "shares" }; // The resource name of the share. Format: memos/{memo}/shares/{share} // The {share} segment is the opaque token used in the share URL. string name = 1 [(google.api.field_behavior) = IDENTIFIER]; // Output only. When this share link was created. google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; // Optional. When set, the share link stops working after this time. // If unset, the link never expires. optional google.protobuf.Timestamp expire_time = 3 [(google.api.field_behavior) = OPTIONAL]; } message CreateMemoShareRequest { // Required. The resource name of the memo to share. // Format: memos/{memo} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; // Required. The share to create. MemoShare memo_share = 2 [(google.api.field_behavior) = REQUIRED]; } message ListMemoSharesRequest { // Required. The resource name of the memo. // Format: memos/{memo} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Memo"} ]; } message ListMemoSharesResponse { // The list of share links. repeated MemoShare memo_shares = 1; } message DeleteMemoShareRequest { // Required. The resource name of the share to delete. // Format: memos/{memo}/shares/{share} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/MemoShare"} ]; } message GetSharedMemoRequest { // Required. The opaque bearer token extracted from the share URL. string share_token = 1 [(google.api.field_behavior) = REQUIRED]; } message GetLinkMetadataRequest { // Required. The link URL. string url = 1 [(google.api.field_behavior) = REQUIRED]; } message BatchGetLinkMetadataRequest { // Required. The link URLs. repeated string urls = 1 [(google.api.field_behavior) = REQUIRED]; } message BatchGetLinkMetadataResponse { // The link metadata list, in the same order as the input URLs. repeated LinkMetadata link_metadata = 1; } message LinkMetadata { // The original link URL. string url = 1; // The link title. string title = 2; // The link description. string description = 3; // The link image URL. string image = 4; }