Internet Engineering Task Force (IETF) N. Jenkins Request for Comments: 8621 Fastmail Updates: 5788 C. Newman Category: Standards Track Oracle ISSN: 2070-1721 August 2019 The JSON Meta Application Protocol (JMAP) for MailAbstract
This document specifies a data model for synchronising email data with a server using the JSON Meta Application Protocol (JMAP). Clients can use this to efficiently search, access, organise, and send messages, and to get push notifications for fast resynchronisation when new messages are delivered or a change is made in another client. Status of This Memo This is an Internet Standards Track document. This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 7841. Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://www.rfc-editor.org/info/rfc8621. Copyright Notice Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5 1.3. Additions to the Capabilities Object . . . . . . . . . . 5 1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 5 1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 7 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 8 1.4. Data Type Support in Different Accounts . . . . . . . . . 8 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 9 1.6. Ids . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 14 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 14 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 14 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 15 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 16 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 17 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 22 3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 22 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 22 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.1. Properties of the Email Object . . . . . . . . . . . . . 23 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 24 4.1.2. Header Fields Parsed Forms . . . . . . . . . . . . . 26 4.1.3. Header Fields Properties . . . . . . . . . . . . . . 32 4.1.4. Body Parts . . . . . . . . . . . . . . . . . . . . . 35 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 44 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 45 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 45 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 46 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 49 4.4.3. Thread Collapsing . . . . . . . . . . . . . . . . . . 50 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 51 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 51 4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 53 4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 54 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 56 4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 58 5. Search Snippets . . . . . . . . . . . . . . . . . . . . . . . 68 5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 69 5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 71
6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 72 6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 73 6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 73 6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 73 6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 73 7. Email Submission . . . . . . . . . . . . . . . . . . . . . . 74 7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 80 7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 80 7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 80 7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 81 7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 81 7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 84 8. Vacation Response . . . . . . . . . . . . . . . . . . . . . . 86 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 87 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 88 9. Security Considerations . . . . . . . . . . . . . . . . . . . 88 9.1. EmailBodyPart Value . . . . . . . . . . . . . . . . . . . 88 9.2. HTML Email Display . . . . . . . . . . . . . . . . . . . 88 9.3. Multiple Part Display . . . . . . . . . . . . . . . . . . 91 9.4. Email Submission . . . . . . . . . . . . . . . . . . . . 91 9.5. Partial Account Access . . . . . . . . . . . . . . . . . 92 9.6. Permission to Send from an Address . . . . . . . . . . . 92 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 93 10.1. JMAP Capability Registration for "mail" . . . . . . . . 93 10.2. JMAP Capability Registration for "submission" . . . . . 93 10.3. JMAP Capability Registration for "vacationresponse" . . 94 10.4. IMAP and JMAP Keywords Registry . . . . . . . . . . . . 94 10.4.1. Registration of JMAP Keyword "$draft" . . . . . . . 95 10.4.2. Registration of JMAP Keyword "$seen" . . . . . . . . 96 10.4.3. Registration of JMAP Keyword "$flagged" . . . . . . 97 10.4.4. Registration of JMAP Keyword "$answered" . . . . . . 98 10.4.5. Registration of "$recent" Keyword . . . . . . . . . 99 10.5. IMAP Mailbox Name Attributes Registry . . . . . . . . . 99 10.5.1. Registration of "inbox" Role . . . . . . . . . . . . 99 10.6. JMAP Error Codes Registry . . . . . . . . . . . . . . . 100 10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 100 10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 100 10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 100 10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 101 10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 101 10.6.6. invalidEmail . . . . . . . . . . . . . . . . . . . . 101 10.6.7. tooManyRecipients . . . . . . . . . . . . . . . . . 102 10.6.8. noRecipients . . . . . . . . . . . . . . . . . . . . 102 10.6.9. invalidRecipients . . . . . . . . . . . . . . . . . 102 10.6.10. forbiddenMailFrom . . . . . . . . . . . . . . . . . 103 10.6.11. forbiddenFrom . . . . . . . . . . . . . . . . . . . 103 10.6.12. forbiddenToSend . . . . . . . . . . . . . . . . . . 103
11. References . . . . . . . . . . . . . . . . . . . . . . . . . 104 11.1. Normative References . . . . . . . . . . . . . . . . . . 104 11.2. Informative References . . . . . . . . . . . . . . . . . 107 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 1081. Introduction
The JSON Meta Application Protocol (JMAP) [RFC8620] is a generic protocol for synchronising data, such as mail, calendars, or contacts between a client and a server. It is optimised for mobile and web environments and aims to provide a consistent interface to different data types. This specification defines a data model for accessing a mail store over JMAP, allowing you to query, read, organise, and submit mail for sending. The data model is designed to allow a server to provide consistent access to the same data via IMAP [RFC3501] as well as JMAP. As in IMAP, a message must belong to a mailbox; however, in JMAP, its id does not change if you move it between mailboxes, and the server may allow it to belong to multiple mailboxes simultaneously (often exposed in a user agent as labels rather than folders). As in IMAP, messages may also be assigned zero or more keywords: short arbitrary strings. These are primarily intended to store metadata to inform client display, such as unread status or whether a message has been replied to. An IANA registry allows common semantics to be shared between clients and extended easily in the future. A message and its replies are linked on the server by a common Thread id. Clients may fetch the list of messages with a particular Thread id to more easily present a threaded or conversational interface. Permissions for message access happen on a per-mailbox basis. Servers may give the user restricted permissions for certain mailboxes, for example, if another user's inbox has been shared as read-only with them.1.1. Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
Type signatures, examples, and property descriptions in this document follow the conventions established in Section 1.1 of [RFC8620]. Data types defined in the core specification are also used in this document. Servers MUST support all properties specified for the new data types defined in this document.1.2. Terminology
This document uses the same terminology as in the core JMAP specification. The terms Mailbox, Thread, Email, SearchSnippet, EmailSubmission and VacationResponse (with that specific capitalisation) are used to refer to the data types defined in this document and instances of those data types. The term message refers to a document in Internet Message Format, as described in [RFC5322]. The Email data type represents messages in the mail store and associated metadata.1.3. Additions to the Capabilities Object
The capabilities object is returned as part of the JMAP Session object; see [RFC8620], Section 2. This document defines three additional capability URIs.1.3.1. urn:ietf:params:jmap:mail
This represents support for the Mailbox, Thread, Email, and SearchSnippet data types and associated API methods. The value of this property in the JMAP session "capabilities" property is an empty object. The value of this property in an account's "accountCapabilities" property is an object that MUST contain the following information on server capabilities and permissions for that account: o maxMailboxesPerEmail: "UnsignedInt|null" The maximum number of Mailboxes (see Section 2) that can be can assigned to a single Email object (see Section 4). This MUST be an integer >= 1, or null for no limit (or rather, the limit is always the number of Mailboxes in the account).
o maxMailboxDepth: "UnsignedInt|null" The maximum depth of the Mailbox hierarchy (i.e., one more than the maximum number of ancestors a Mailbox may have), or null for no limit. o maxSizeMailboxName: "UnsignedInt" The maximum length, in (UTF-8) octets, allowed for the name of a Mailbox. This MUST be at least 100, although it is recommended servers allow more. o maxSizeAttachmentsPerEmail: "UnsignedInt" The maximum total size of attachments, in octets, allowed for a single Email object. A server MAY still reject the import or creation of an Email with a lower attachment size total (for example, if the body includes several megabytes of text, causing the size of the encoded MIME structure to be over some server- defined limit). Note that this limit is for the sum of unencoded attachment sizes. Users are generally not knowledgeable about encoding overhead, etc., nor should they need to be, so marketing and help materials normally tell them the "max size attachments". This is the unencoded size they see on their hard drive, so this capability matches that and allows the client to consistently enforce what the user understands as the limit. The server may separately have a limit for the total size of the message [RFC5322], created by combining the attachments (often base64 encoded) with the message headers and bodies. For example, suppose the server advertises "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The enforced server limit may be for a message size of 70000000 octets. Even with base64 encoding and a 2 MB HTML body, 50 MB attachments would fit under this limit. o emailQuerySortOptions: "String[]" A list of all the values the server supports for the "property" field of the Comparator object in an "Email/query" sort (see Section 4.4.2). This MAY include properties the client does not recognise (for example, custom properties specified in a vendor extension). Clients MUST ignore any unknown properties in the list.
o mayCreateTopLevelMailbox: "Boolean" If true, the user may create a Mailbox (see Section 2) in this account with a null parentId. (Permission for creating a child of an existing Mailbox is given by the "myRights" property on that Mailbox.)1.3.2. urn:ietf:params:jmap:submission
This represents support for the Identity and EmailSubmission data types and associated API methods. The value of this property in the JMAP session "capabilities" property is an empty object. The value of this property in an account's "accountCapabilities" property is an object that MUST contain the following information on server capabilities and permissions for that account: o maxDelayedSend: "UnsignedInt" The number in seconds of the maximum delay the server supports in sending (see the EmailSubmission object description). This is 0 if the server does not support delayed send. o submissionExtensions: "String[String[]]" The set of SMTP submission extensions supported by the server, which the client may use when creating an EmailSubmission object (see Section 7). Each key in the object is the "ehlo-name", and the value is a list of "ehlo-args". A JMAP implementation that talks to a submission server [RFC6409] SHOULD have a configuration setting that allows an administrator to modify the set of submission EHLO capabilities it may expose on this property. This allows a JMAP server to easily add access to a new submission extension without code changes. By default, the JMAP server should hide EHLO capabilities that have to do with the transport mechanism and thus are only relevant to the JMAP server (for example, PIPELINING, CHUNKING, or STARTTLS). Examples of Submission extensions to include: * FUTURERELEASE [RFC4865] * SIZE [RFC1870] * DSN [RFC3461] * DELIVERYBY [RFC2852]
* MT-PRIORITY [RFC6710] A JMAP server MAY advertise an extension and implement the semantics of that extension locally on the JMAP server even if a submission server used by JMAP doesn't implement it. The full IANA registry of submission extensions can be found at <https://www.iana.org/assignments/mail-parameters>.1.3.3. urn:ietf:params:jmap:vacationresponse
This represents support for the VacationResponse data type and associated API methods. The value of this property is an empty object in both the JMAP session "capabilities" property and an account's "accountCapabilities" property.1.4. Data Type Support in Different Accounts
The server MUST include the appropriate capability strings as keys in the "accountCapabilities" property of any account with which the user may use the data types represented by that URI. Supported data types may differ between accounts the user has access to. For example, in the user's personal account, they may have access to all three sets of data, but in a shared account, they may only have data for "urn:ietf:params:jmap:mail". This means they can access Mailbox/Thread/Email data in the shared account but are not allowed to send as that account (and so do not have access to Identity/ EmailSubmission objects) or view/set its VacationResponse.1.5. Push
Servers MUST support the JMAP push mechanisms, as specified in [RFC8620], Section 7, to receive notifications when the state changes for any of the types defined in this specification. In addition, servers that implement the "urn:ietf:params:jmap:mail" capability MUST support pushing state changes for a type called "EmailDelivery". There are no methods to act on this type; it only exists as part of the push mechanism. The state string for this MUST change whenever a new Email is added to the store, but it SHOULD NOT change upon any other change to the Email objects, for example, if one is marked as read or deleted. Clients in battery-constrained environments may wish to delay fetching changes initiated by the user but fetch new Emails immediately so they can notify the user. To do this, they can register for pushes for the EmailDelivery type rather than the Email type (as defined in Section 4).
1.5.1. Example
The client has registered for push notifications (see [RFC8620]) just for the EmailDelivery type. The user marks an Email as read on another device, causing the state string for the Email type to change; however, as nothing new was added to the store, the EmailDelivery state does not change and nothing is pushed to the client. A new message arrives in the user's inbox, again causing the Email state to change. This time, the EmailDelivery state also changes, and a StateChange object is pushed to the client with the new state string. The client may then resync to fetch the new Email immediately.1.6. Ids
If a JMAP Mail server also provides an IMAP interface to the data and supports IMAP Extension for Object Identifiers [RFC8474], the ids SHOULD be the same for Mailbox, Thread, and Email objects in JMAP.2. Mailboxes
A Mailbox represents a named set of Email objects. This is the primary mechanism for organising messages within an account. It is analogous to a folder or a label in other systems. A Mailbox may perform a certain role in the system; see below for more details. For compatibility with IMAP, an Email MUST belong to one or more Mailboxes. The Email id does not change if the Email changes Mailboxes. A *Mailbox* object has the following properties: o id: "Id" (immutable; server-set) The id of the Mailbox. o name: "String" User-visible name for the Mailbox, e.g., "Inbox". This MUST be a Net-Unicode string [RFC5198] of at least 1 character in length, subject to the maximum size given in the capability object. There MUST NOT be two sibling Mailboxes with both the same parent and the same name. Servers MAY reject names that violate server policy (e.g., names containing a slash (/) or control characters).
o parentId: "Id|null" (default: null) The Mailbox id for the parent of this Mailbox, or null if this Mailbox is at the top level. Mailboxes form acyclic graphs (forests) directed by the child-to-parent relationship. There MUST NOT be a loop. o role: "String|null" (default: null) Identifies Mailboxes that have a particular common purpose (e.g., the "inbox"), regardless of the "name" property (which may be localised). This value is shared with IMAP (exposed in IMAP via the SPECIAL- USE extension [RFC6154]). However, unlike in IMAP, a Mailbox MUST only have a single role, and there MUST NOT be two Mailboxes in the same account with the same role. Servers providing IMAP access to the same data are encouraged to enforce these extra restrictions in IMAP as well. Otherwise, modifying the IMAP attributes to ensure compliance when exposing the data over JMAP is implementation dependent. The value MUST be one of the Mailbox attribute names listed in the IANA "IMAP Mailbox Name Attributes" registry at <https://www.iana.org/assignments/imap-mailbox-name-attributes/>, as established in [RFC8457], converted to lowercase. New roles may be established here in the future. An account is not required to have Mailboxes with any particular roles. o sortOrder: "UnsignedInt" (default: 0) Defines the sort order of Mailboxes when presented in the client's UI, so it is consistent between devices. The number MUST be an integer in the range 0 <= sortOrder < 2^31. A Mailbox with a lower order should be displayed before a Mailbox with a higher order (that has the same parent) in any Mailbox listing in the client's UI. Mailboxes with equal order SHOULD be sorted in alphabetical order by name. The sorting should take into account locale-specific character order convention. o totalEmails: "UnsignedInt" (server-set) The number of Emails in this Mailbox.
o unreadEmails: "UnsignedInt" (server-set) The number of Emails in this Mailbox that have neither the "$seen" keyword nor the "$draft" keyword. o totalThreads: "UnsignedInt" (server-set) The number of Threads where at least one Email in the Thread is in this Mailbox. o unreadThreads: "UnsignedInt" (server-set) An indication of the number of "unread" Threads in the Mailbox. For compatibility with existing implementations, the way "unread Threads" is determined is not mandated in this document. The simplest solution to implement is simply the number of Threads where at least one Email in the Thread is both in this Mailbox and has neither the "$seen" nor "$draft" keywords. However, a quality implementation will return the number of unread items the user would see if they opened that Mailbox. A Thread is shown as unread if it contains any unread Emails that will be displayed when the Thread is opened. Therefore, "unreadThreads" should be the number of Threads where at least one Email in the Thread has neither the "$seen" nor the "$draft" keyword AND at least one Email in the Thread is in this Mailbox. Note that the unread Email does not need to be the one in this Mailbox. In addition, the trash Mailbox (that is, a Mailbox whose "role" is "trash") requires special treatment: 1. Emails that are *only* in the trash (and no other Mailbox) are ignored when calculating the "unreadThreads" count of other Mailboxes. 2. Emails that are *not* in the trash are ignored when calculating the "unreadThreads" count for the trash Mailbox. The result of this is that Emails in the trash are treated as though they are in a separate Thread for the purposes of unread counts. It is expected that clients will hide Emails in the trash when viewing a Thread in another Mailbox, and vice versa. This allows you to delete a single Email to the trash out of a Thread. For example, suppose you have an account where the entire contents is a single Thread with 2 Emails: an unread Email in the trash and a read Email in the inbox. The "unreadThreads" count would be 1 for the trash and 0 for the inbox.
o myRights: "MailboxRights" (server-set) The set of rights (Access Control Lists (ACLs)) the user has in relation to this Mailbox. These are backwards compatible with IMAP ACLs, as defined in [RFC4314]. A *MailboxRights* object has the following properties: * mayReadItems: "Boolean" If true, the user may use this Mailbox as part of a filter in an "Email/query" call, and the Mailbox may be included in the "mailboxIds" property of Email objects. Email objects may be fetched if they are in *at least one* Mailbox with this permission. If a sub-Mailbox is shared but not the parent Mailbox, this may be false. Corresponds to IMAP ACLs "lr" (if mapping from IMAP, both are required for this to be true). * mayAddItems: "Boolean" The user may add mail to this Mailbox (by either creating a new Email or moving an existing one). Corresponds to IMAP ACL "i". * mayRemoveItems: "Boolean" The user may remove mail from this Mailbox (by either changing the Mailboxes of an Email or destroying the Email). Corresponds to IMAP ACLs "te" (if mapping from IMAP, both are required for this to be true). * maySetSeen: "Boolean" The user may add or remove the "$seen" keyword to/from an Email. If an Email belongs to multiple Mailboxes, the user may only modify "$seen" if they have this permission for *all* of the Mailboxes. Corresponds to IMAP ACL "s". * maySetKeywords: "Boolean" The user may add or remove any keyword other than "$seen" to/ from an Email. If an Email belongs to multiple Mailboxes, the user may only modify keywords if they have this permission for *all* of the Mailboxes. Corresponds to IMAP ACL "w". * mayCreateChild: "Boolean" The user may create a Mailbox with this Mailbox as its parent. Corresponds to IMAP ACL "k".
* mayRename: "Boolean" The user may rename the Mailbox or make it a child of another Mailbox. Corresponds to IMAP ACL "x" (although this covers both rename and delete permissions). * mayDelete: "Boolean" The user may delete the Mailbox itself. Corresponds to IMAP ACL "x" (although this covers both rename and delete permissions). * maySubmit: "Boolean" Messages may be submitted directly to this Mailbox. Corresponds to IMAP ACL "p". o isSubscribed: "Boolean" Has the user indicated they wish to see this Mailbox in their client? This SHOULD default to false for Mailboxes in shared accounts the user has access to and true for any new Mailboxes created by the user themself. This MUST be stored separately per user where multiple users have access to a shared Mailbox. A user may have permission to access a large number of shared accounts, or a shared account with a very large set of Mailboxes, but only be interested in the contents of a few of these. Clients may choose to only display Mailboxes where the "isSubscribed" property is set to true, and offer a separate UI to allow the user to see and subscribe/unsubscribe from the full set of Mailboxes. However, clients MAY choose to ignore this property, either entirely for ease of implementation or just for an account where "isPersonal" is true (indicating it is the user's own rather than a shared account). This property corresponds to IMAP [RFC3501] mailbox subscriptions. For IMAP compatibility, an Email in both the trash and another Mailbox SHOULD be treated by the client as existing in both places (i.e., when emptying the trash, the client should just remove it from the trash Mailbox and leave it in the other Mailbox). The following JMAP methods are supported.
2.1. Mailbox/get
This is a standard "/get" method as described in [RFC8620], Section 5.1. The "ids" argument may be "null" to fetch all at once.2.2. Mailbox/changes
This is a standard "/changes" method as described in [RFC8620], Section 5.2 but with one extra argument to the response: o updatedProperties: "String[]|null" If only the "totalEmails", "unreadEmails", "totalThreads", and/or "unreadThreads" Mailbox properties have changed since the old state, this will be the list of properties that may have changed. If the server is unable to tell if only counts have changed, it MUST just be null. Since counts frequently change but other properties are generally only changed rarely, the server can help the client optimise data transfer by keeping track of changes to Email/Thread counts separate from other state changes. The "updatedProperties" array may be used directly via a back-reference in a subsequent "Mailbox/get" call in the same request, so only these properties are returned if nothing else has changed.2.3. Mailbox/query
This is a standard "/query" method as described in [RFC8620], Section 5.5 but with the following additional request argument: o sortAsTree: "Boolean" (default: false) If true, when sorting the query results and comparing Mailboxes A and B: * If A is an ancestor of B, it always comes first regardless of the sort comparators. Similarly, if A is descendant of B, then B always comes first. * Otherwise, if A and B do not share a "parentId", find the nearest ancestors of each that do have the same "parentId" and compare the sort properties on those Mailboxes instead. The result of this is that the Mailboxes are sorted as a tree according to the parentId properties, with each set of children with a common parent sorted according to the standard sort comparators.
o filterAsTree: "Boolean" (default: false) If true, a Mailbox is only included in the query if all its ancestors are also included in the query according to the filter. A *FilterCondition* object has the following properties, any of which may be omitted: o parentId: "Id|null" The Mailbox "parentId" property must match the given value exactly. o name: "String" The Mailbox "name" property contains the given string. o role: "String|null" The Mailbox "role" property must match the given value exactly. o hasAnyRole: "Boolean" If true, a Mailbox matches if it has any non-null value for its "role" property. o isSubscribed: "Boolean" The "isSubscribed" property of the Mailbox must be identical to the value given to match the condition. A Mailbox object matches the FilterCondition if and only if all of the given conditions match. If zero properties are specified, it is automatically true for all objects. The following Mailbox properties MUST be supported for sorting: o "sortOrder" o "name"2.4. Mailbox/queryChanges
This is a standard "/queryChanges" method as described in [RFC8620], Section 5.6.
2.5. Mailbox/set
This is a standard "/set" method as described in [RFC8620], Section 5.3 but with the following additional request argument: o onDestroyRemoveEmails: "Boolean" (default: false) If false, any attempt to destroy a Mailbox that still has Emails in it will be rejected with a "mailboxHasEmail" SetError. If true, any Emails that were in the Mailbox will be removed from it, and if in no other Mailboxes, they will be destroyed when the Mailbox is destroyed. The following extra SetError types are defined: For "destroy": o "mailboxHasChild": The Mailbox still has at least one child Mailbox. The client MUST remove these before it can delete the parent Mailbox. o "mailboxHasEmail": The Mailbox has at least one Email assigned to it, and the "onDestroyRemoveEmails" argument was false.
2.6. Example
Fetching all Mailboxes in an account: [[ "Mailbox/get", { "accountId": "u33084183", "ids": null }, "0" ]] And the response: [[ "Mailbox/get", { "accountId": "u33084183", "state": "78540", "list": [{ "id": "MB23cfa8094c0f41e6", "name": "Inbox", "parentId": null, "role": "inbox", "sortOrder": 10, "totalEmails": 16307, "unreadEmails": 13905, "totalThreads": 5833, "unreadThreads": 5128, "myRights": { "mayAddItems": true, "mayRename": false, "maySubmit": true, "mayDelete": false, "maySetKeywords": true, "mayRemoveItems": true, "mayCreateChild": true, "maySetSeen": true, "mayReadItems": true }, "isSubscribed": true }, { "id": "MB674cc24095db49ce", "name": "Important mail", ... }, ... ], "notFound": [] }, "0" ]]
Now suppose an Email is marked read, and we get a push update that the Mailbox state has changed. You might fetch the updates like this: [[ "Mailbox/changes", { "accountId": "u33084183", "sinceState": "78540" }, "0" ], [ "Mailbox/get", { "accountId": "u33084183", "#ids": { "resultOf": "0", "name": "Mailbox/changes", "path": "/created" } }, "1" ], [ "Mailbox/get", { "accountId": "u33084183", "#ids": { "resultOf": "0", "name": "Mailbox/changes", "path": "/updated" }, "#properties": { "resultOf": "0", "name": "Mailbox/changes", "path": "/updatedProperties" } }, "2" ]]
This fetches the list of ids for created/updated/destroyed Mailboxes, then using back-references, it fetches the data for just the created/ updated Mailboxes in the same request. The response may look something like this: [[ "Mailbox/changes", { "accountId": "u33084183", "oldState": "78541", "newState": "78542", "hasMoreChanges": false, "updatedProperties": [ "totalEmails", "unreadEmails", "totalThreads", "unreadThreads" ], "created": [], "updated": ["MB23cfa8094c0f41e6"], "destroyed": [] }, "0" ], [ "Mailbox/get", { "accountId": "u33084183", "state": "78542", "list": [], "notFound": [] }, "1" ], [ "Mailbox/get", { "accountId": "u33084183", "state": "78542", "list": [{ "id": "MB23cfa8094c0f41e6", "totalEmails": 16307, "unreadEmails": 13903, "totalThreads": 5833, "unreadThreads": 5127 }], "notFound": [] }, "2" ]]
Here's an example where we try to rename one Mailbox and destroy another: [[ "Mailbox/set", { "accountId": "u33084183", "ifInState": "78542", "update": { "MB674cc24095db49ce": { "name": "Maybe important mail" } }, "destroy": [ "MB23cfa8094c0f41e6" ] }, "0" ]] Suppose the rename succeeds, but we don't have permission to destroy the Mailbox we tried to destroy; we might get back: [[ "Mailbox/set", { "accountId": "u33084183", "oldState": "78542", "newState": "78549", "updated": { "MB674cc24095db49ce": null }, "notDestroyed": { "MB23cfa8094c0f41e6": { "type": "forbidden" } } }, "0" ]]3. Threads
Replies are grouped together with the original message to form a Thread. In JMAP, a Thread is simply a flat list of Emails, ordered by date. Every Email MUST belong to a Thread, even if it is the only Email in the Thread. The exact algorithm for determining whether two Emails belong to the same Thread is not mandated in this spec to allow for compatibility with different existing systems. For new implementations, it is suggested that two messages belong in the same Thread if both of the following conditions apply: 1. An identical message id [RFC5322] appears in both messages in any of the Message-Id, In-Reply-To, and References header fields.
2. After stripping automatically added prefixes such as "Fwd:", "Re:", "[List-Tag]", etc., and ignoring white space, the subjects are the same. This avoids the situation where a person replies to an old message as a convenient way of finding the right recipient to send to but changes the subject and starts a new conversation. If messages are delivered out of order for some reason, a user may have two Emails in the same Thread but without headers that associate them with each other. The arrival of a third Email may provide the missing references to join them all together into a single Thread. Since the "threadId" of an Email is immutable, if the server wishes to merge the Threads, it MUST handle this by deleting and reinserting (with a new Email id) the Emails that change "threadId". A *Thread* object has the following properties: o id: "Id" (immutable; server-set) The id of the Thread. o emailIds: "Id[]" (server-set) The ids of the Emails in the Thread, sorted by the "receivedAt" date of the Email, oldest first. If two Emails have an identical date, the sort is server dependent but MUST be stable (sorting by id is recommended). The following JMAP methods are supported.
3.1. Thread/get
This is a standard "/get" method as described in [RFC8620], Section 5.1.3.1.1. Example
Request: [[ "Thread/get", { "accountId": "acme", "ids": ["f123u4", "f41u44"] }, "#1" ]] with response: [[ "Thread/get", { "accountId": "acme", "state": "f6a7e214", "list": [ { "id": "f123u4", "emailIds": [ "eaa623", "f782cbb"] }, { "id": "f41u44", "emailIds": [ "82cf7bb" ] } ], "notFound": [] }, "#1" ]]3.2. Thread/changes
This is a standard "/changes" method as described in [RFC8620], Section 5.2.