<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-ietf-jmap-essential-01" submissionType="IETF" category="info" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="JMAP Essential">JMAP Essential Profile</title><seriesInfo value="draft-ietf-jmap-essential-01" stream="IETF" status="informational" name="Internet-Draft"></seriesInfo>
<author role="editor" initials="J.M." surname="Baum" fullname="Joris Baum"><organization>audriga</organization><address><postal><street>Alter Schlachthof 57</street>
<city>Karlsruhe </city>
<code>76137</code>
<country>Germany</country>
</postal><email>joris@audriga.com</email>
<uri>https://www.audriga.com</uri>
</address></author><author role="editor" initials="H.J." surname="Happel" fullname="Hans-Joerg"><organization>audriga</organization><address><postal><street>Alter Schlachthof 57</street>
<city>Karlsruhe </city>
<code>76137</code>
<country>Germany</country>
</postal><email>hans-joerg@audriga.com</email>
<uri>https://www.audriga.com</uri>
</address></author><date year="2024" month="August" day="1"></date>
<area>Applications</area>
<workgroup>JMAP</workgroup>
<keyword>jmap</keyword>
<keyword>data portability</keyword>
<keyword>migration</keyword>

<abstract>
<t>JMAP (RFC8620) is a generic, efficient, mobile friendly and scalable protocol that can be used for data of any type. This makes it a good fit for migrations or data portability use cases that are focusing on data import and export. However, due to its large set of features, it is also quite complex, which makes it difficult to explore new application domains in practice. The goal of this document is to provide guidelines on implementing essential parts of JMAP for a much lower entry barrier and more efficient implementation of the protocol.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>JMAP <xref target="RFC8620"></xref> is designed to be a generic, efficient, mobile friendly and scalable protocol. Achieving these design goals comes with high complexity, which can be a barrier to its adoption in new application domains.</t>
<t>Migration and data portability is about moving arbitrary user data between services. JMAP is a particularly good fit for satisfying basic data portability requirements. It can be used as an open protocol in front of an application service, exposing data of any kind. However, implementing the full JMAP spec can be complicated, which makes it difficult to explore new application domains in practice.</t>
<t>For basic data portability requirements, users need to be able to export their data from a product or import it into a product in real time. Providers aiming to support JMAP for their services to meet data portability requirements likely do not want to implement the full feature set defined by JMAP. Currently, there is no guidance on how to implement only parts of RFC8620's features.</t>
<t>This specification aims to provide guidance to identify essential parts of the JMAP spec for more rapid development. It focuses on the minimal implementation required for basic data portability. For the sole purpose of providing very basic data portability, there is no need to implement all parts of the JMAP protocol. It should be noted that this essential profile will result in an inefficient protocol compared to the full JMAP specification. In a second iteration, developers could then extend this basic version of JMAP by adding advanced features, such as those for additional performance improvements.</t>

<section anchor="conventions-used-in-this-document"><name>Conventions Used In This Document</name>
<t>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL
NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;,
&quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only when,
they appear in all capitals, as shown here.</t>
<t>The definitions of JSON keys and datatypes in the document follow
the conventions described in the core JMAP specification <xref target="RFC8620"></xref>.</t>
</section>
</section>

<section anchor="outline-on-rfc8620-features"><name>Outline on RFC8620 Features</name>

<section anchor="use-cases"><name>Use cases</name>
<t>Not all features of <xref target="RFC8620"></xref> are required for basic use cases for which efficiency is not a crucial requirement. This document focuses on two main scenarios: JMAP Essential Export and JMAP Essential Import.</t>

<section anchor="jmap-bare-minimum"><name>JMAP Bare Minimum</name>
<t>JMAP Bare Minimum defines the absolute minimal set of features required to implement JMAP. It serves as a foundational blueprint, ensuring that developers understand the essential components of JMAP. Although this implementation is unlikely to meet practical needs on its own, it establishes the groundwork for more complex use cases.</t>
</section>

<section anchor="jmap-essential-export"><name>JMAP Essential Export</name>
<t>JMAP Essential Export use cases require that data can be exported from an application server. This scenario is common in data portability contexts where data needs to be transferred out of a server, such as when a user wants to move their data from one service provider to another. The emphasis here is on the necessary methods for extracting data, ensuring that the exported data is in a usable format for subsequent import or analysis.</t>
</section>

<section anchor="jmap-essential-import"><name>JMAP Essential Import</name>
<t>JMAP Essential Import use cases, on the other hand require that data can be imported into an application server. This is crucial for scenarios where data is being migrated into a new system from an external source. The primary focus is on the methods needed for receiving and integrating data, such as uploading binary data and processing incoming data formats. This use case ensures that the server can seamlessly incorporate external data into its existing structure.</t>
</section>
</section>

<section anchor="session-resource"><name>Session Resource</name>
<t>For a lot of basic portability use cases for existing application services
the following constraints are typically acceptable:</t>

<ul spacing="compact">
<li>A single user login is tied to a single user account (this is then also the primary account).</li>
<li>Shared data shall not be exposed over the API. The API will only provide access to data owned by the current user.</li>
<li>Each user has the same set of capabilities and restrictions (e.g.  <tt>maxMailboxesPerEmail</tt>).</li>
<li><tt>apiUrl</tt>, <tt>downloadUrl</tt>, <tt>uploadUrl</tt> and <tt>eventSourceUrl</tt> are the same for ever user.</li>
</ul>
<t>For use cases adhering to those restrictions, the session resource can be modeled as a simple JSON file that only contains constant values. This JSON then specifies a single <tt>accountId</tt> which is then assigned the value &quot;self&quot;.</t>
</section>

<section anchor="structured-data-exchange"><name>Structured Data exchange</name>
<t>While batching improves performance considerably, it imposes additional
implementation effort on developers. It can be left out as it is not essential, in particular for basic portability use cases.</t>
</section>

<section anchor="standard-methods-and-naming-convention"><name>Standard Methods and Naming Convention</name>
<t>JMAP core defines 6 standard methods. Not all JMAP Methods are required to provide essential functionality.
For some use-cases where the data is expected to be small, <strong>/set</strong> and <strong>/get</strong>
should be enough. In case a large amount of
data shall be supported, paging can be achieved via the <strong>/query</strong> method. Note
that some specifications require specific IDs for /get .</t>
<t><strong>/changes</strong>, <strong>/copy</strong> as well as <strong>/queryChanges</strong> are not required as all
data can already be imported and exported with above's three methods.</t>
</section>

<section anchor="binary-data"><name>Binary Data</name>
<t>The advanced Blob/copy method call is not essential. Not all applications support attachments for their specific kind of data. Additionally, some data types allow having attachments as base64 encoded strings inside a JMAP object. In those cases, it is not required to implement a download or upload endpoint.</t>
</section>
</section>

<section anchor="implementation-details"><name>Implementation Details</name>

<section anchor="overview-tables"><name>Overview Tables</name>
<t>Tables 1-4 list the required features for a minimal implementation of JMAP Essential Profile in more detail. The next section provides more implementation considerations. It references the three use cases defined in <xref target="use-cases"></xref>. The value for each table cell details what is required at minimum for an implementation:</t>

<ul spacing="compact">
<li><strong>-</strong> : Not required</li>
<li><strong>constant value(s)</strong>: RFC 8620 requires that at least a constant value will be returned by an application</li>
<li><strong>error response</strong>: RFC 8620 requires that a minimal implementation emits an error response signalling that a feature is not supported</li>
<li><strong>&quot;&quot;</strong>: Same as JMAP Bare Minimum</li>
</ul>
<table><name>Session Object features essential for basic use cases
</name>
<thead>
<tr>
<th>JMAP Core Feature</th>
<th>JMAP Bare Minimum</th>
<th>JMAP Essential Export</th>
<th>JMAP Essential Import</th>
</tr>
</thead>

<tbody>
<tr>
<td>Session Object</td>
<td>constant values<sup>1</sup></td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Service Autodiscovery</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>
</tbody>
</table><table><name>Structured Data Exchange features essential for basic use cases
</name>
<thead>
<tr>
<th>JMAP Core Feature</th>
<th>JMAP Bare Minimum</th>
<th>JMAP Essential Export</th>
<th>JMAP Essential Import</th>
</tr>
</thead>

<tbody>
<tr>
<td>Invocation (all properties)</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Request (using)</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Request (methodCalls)</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Request (createdIds)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Response (methodResponses)</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Response (createdIds)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Response (sessionState)</td>
<td>constant value</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Errors</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>References to Previous Method Results</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Localisation of User-Visible String</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>
</tbody>
</table><table><name>Method features essential for basic use cases
</name>
<thead>
<tr>
<th>JMAP Core Feature</th>
<th>JMAP Bare Minimum</th>
<th>JMAP Essential Export</th>
<th>JMAP Essential Import</th>
</tr>
</thead>

<tbody>
<tr>
<td>Core/echo</td>
<td>required</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Request</td>
<td>error response</td>
<td>required</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Request (accountId)</td>
<td>-</td>
<td>constant value<sup>1</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Request (ids)</td>
<td>-</td>
<td>error response, required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Request (properties)</td>
<td>-</td>
<td>error response</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Response</td>
<td>-</td>
<td>required</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Response (accountId)</td>
<td>-</td>
<td>constant value<sup>1</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Response (state)</td>
<td>-</td>
<td>constant value</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Response (list)</td>
<td>-</td>
<td>required</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/get method Response (notFound)</td>
<td>-</td>
<td>constant value, required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/changes method (full)</td>
<td>error response</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/set method Request</td>
<td>error response</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/set method Request (accountId)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value<sup>1</sup></td>
</tr>

<tr>
<td>/set method Request (ifInState)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value</td>
</tr>

<tr>
<td>/set method Request (create, only single id)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/set method Request (create, multiple ids)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/set method Request (update)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>error response</td>
</tr>

<tr>
<td>/set method Request (destroy)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>error response</td>
</tr>

<tr>
<td>/set method Response</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/set method Response (accountId)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value<sup>1</sup></td>
</tr>

<tr>
<td>/set method Response (oldState)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value</td>
</tr>

<tr>
<td>/set method Response (newState)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value</td>
</tr>

<tr>
<td>/set method Response (created)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/set method Response (updated)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value</td>
</tr>

<tr>
<td>/set method Response (destroyed)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>constant value</td>
</tr>

<tr>
<td>/set method Response (notCreated)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/set method Response (notUpdated)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>error response</td>
</tr>

<tr>
<td>/set method Response (notDestroyed)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>error response</td>
</tr>

<tr>
<td>/set method SetError</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required</td>
</tr>

<tr>
<td>/copy method (full)</td>
<td>error response</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request</td>
<td>error response</td>
<td>required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (accountId)</td>
<td>-</td>
<td>constant value for listing /paging<sup>1,2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (filter)</td>
<td>-</td>
<td>error response for listing /paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (sort)</td>
<td>-</td>
<td>error response for listing /paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (position)</td>
<td>-</td>
<td>error response for listing, required for paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (anchor)</td>
<td>-</td>
<td>error response for listing /paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (anchorOffset)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (limit)</td>
<td>-</td>
<td>error response for listing /paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Request (calculateTotal)</td>
<td>-</td>
<td>error response for listing, required for paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response</td>
<td>-</td>
<td>required for listing /paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (accountId)</td>
<td>-</td>
<td>constant value for listing /paging<sup>1,2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (queryState)</td>
<td>-</td>
<td>required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (canCalculateChanges)</td>
<td>-</td>
<td>constant value</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (position)</td>
<td>-</td>
<td>required for paging</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (ids)</td>
<td>-</td>
<td>required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (total)</td>
<td>-</td>
<td>required for paging</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Response (limit)</td>
<td>-</td>
<td>required for listing/paging<sup>2</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method FilterCondition</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method FilterOperator</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/query method Comparator</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>/queryChanges method (full)</td>
<td>error response</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>
</tbody>
</table><table><name>Blob and Push features essential for basic use cases
</name>
<thead>
<tr>
<th>JMAP Core Feature</th>
<th>JMAP Bare Minimum</th>
<th>JMAP Essential Export</th>
<th>JMAP Essential Import</th>
</tr>
</thead>

<tbody>
<tr>
<td>Uploading Binary Data</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>required for importing attachments<sup>3</sup></td>
</tr>

<tr>
<td>Downloading Binary Data</td>
<td>-</td>
<td>required for exporting attachments<sup>3</sup></td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Blob/copy (full)</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>

<tr>
<td>Push</td>
<td>-</td>
<td>&quot;&quot;</td>
<td>&quot;&quot;</td>
</tr>
</tbody>
</table>
<ul spacing="compact">
<li>1: Can be modeled as a JSON file with only constant values for a lot of scenarios. <xref target="session-resource"></xref> details for which use cases such a JSON file should be sufficient.</li>
<li>2: maxObjectsInGet might not be sufficiently high in some products. In this case, /query is required. Similarly, paging might be required in case a query might exceed the limit for the maximum amount of results for /query.</li>
<li>3: Some data types allow having attachments as base64 enconded strings inside a JMAP object. In those cases it is not necessary to implement a download or upload endpoint.</li>
</ul>
</section>

<section anchor="implementation-considerations"><name>Implementation considerations</name>

<section anchor="all-use-cases"><name>All use cases</name>

<section anchor="bare-minimum"><name>Bare Minimum</name>
<t>For a bare minimum Session object, choose the following to return a static JSON, which is the same for every user:</t>

<ul spacing="compact">
<li>maxSizeRequest</li>
<li>maxConcurrentRequests</li>
<li>apiUrl</li>
<li>additional capabilities and their configuration</li>
</ul>

<sourcecode type="json"><![CDATA[{
  "capabilities": {
    "urn:ietf:params:jmap:core": {
      "maxSizeUpload": 0,
      "maxConcurrentUpload": 0,
      "maxSizeRequest": <maxSizeRequest>,
      "maxConcurrentRequests": <maxConcurrentRequests>,
      "maxCallsInRequest": 1,
      "maxObjectsInGet": 0,
      "maxObjectsInSet": 0,
      "collationAlgorithms": []
    },
    "urn:ietf:params:jmap:<other-capability>": {},
    ...
  },
  "accounts": {
    "self": {
      "name": "",
      "isPersonal": true,
      "isReadOnly": true,
      "accountCapabilities": {
        "urn:ietf:params:jmap:<other-capability>": {
          "<key>": <value>,
          ...
        },
        ...
      }
    }
  },
  "primaryAccounts": {
    "urn:ietf:params:jmap:<other-capability>": "self"
  },
  "username": "",
  "apiUrl": "<apiUrl>",
  "downloadUrl": "",
  "uploadUrl": "",
  "eventSourceUrl": "",
  "state": ""
}
]]>
</sourcecode>
<t>Implement the following for Structured Data Exchange:</t>

<ul spacing="compact">
<li>Invocation (all properties) - <strong>required</strong></li>
<li>Request (using) - <strong>required</strong>; Make sure to keep capabilities simple to keep implementation complexity low.</li>
<li>Request (methodCalls) - <strong>required</strong>; No logic for batching, because maxCallsInRequest is set to 1.</li>
<li>Request (createdIds) - not required; Only required in conjunction with batching.</li>
<li>Response (methodResponses) - <strong>required</strong></li>
<li>Response (createdIds) - not required; See createIds.</li>
<li>Response (sessionState) - <em>constant value</em>; Set to &quot;&quot;.</li>
<li>Errors - <strong>required</strong>; Implement serverFail as the most basic of all error types. Make sure to implement its description property to clarify the error context. Additional error types are very useful for debugging.</li>
<li>References to Previous Method Results - not required; This is, because batching is not advertised.</li>
<li>Localisation of User-Visible String - not required</li>
</ul>
<t>For Methods:</t>

<ul spacing="compact">
<li>Core/echo method (full) - <strong>required</strong>; Core/echo is useful for basic connection testing and requires very low implementation effort.</li>
<li>/get method (full) - <em>error response</em>; always reply with <tt>requestTooLarge</tt> error.</li>
<li>/set method (full) - <em>error response</em>; always reply with <tt>accountReadOnly</tt> error.</li>
<li>/changes method (full) - <em>error response</em>; always reply with <tt>cannotCalculateChanges</tt> error.</li>
<li>/copy method (full) - <em>error response</em>; always reply with <tt>serverFail</tt> error. Its description should explain that the method is merely not supported.</li>
<li>/query method (full) - <em>error response</em>; same as /copy.</li>
<li>/queryChanges method (full) - <em>error response</em>; always reply with <tt>cannotCalculateChanges</tt> error.</li>
</ul>
<t>Note that there are some caveats when implementing the bare minimum of JMAP. Setting downloadUrl and uploadUrl to an empty string might be incompatible with some existing JMAP implementations as they are defined as &quot;MUST contain variables&quot;, which an empty string does not contain. Also, some errors recommended in this document, like serverFail or invalidArgument, typically signal to clients that something unexpected has happened, when in fact servers can expect clients to call any standard JMAP method or property. Due to this the description property is used to clarify the context of errors like serverFail and invalidArgument. However, its value is a free-text string and therefore not machine-processable in an interoperable way.</t>
</section>

<section anchor="exporting-data"><name>Exporting data</name>
<t>The Session Object now additional requires:</t>

<ul spacing="compact">
<li>maxObjectsInGet</li>
</ul>
<t>For Methods:</t>

<ul spacing="compact">
<li>/get method Request - <strong>required</strong></li>
<li>/get method Request (accountId) - <em>constant value</em>; Is always &quot;self&quot;. Can therefore be ignored on the server side.</li>
<li>/get method Request (ids) - <em>error response</em>; Always respond with <tt>invalidArguments</tt>. Its description property MUST explain that the ids property is merely not supported. The ids property is formally required by RFC8920, but in practice applications will not use the ids fields without retrieving ids via /query or /set first.</li>
<li>/get method Request (properties) - <em>error response</em>; Always respond with <tt>invalidArguments</tt>. Its description property MUST explain that the properties property is merely not supported.</li>
<li>/get method Response</li>
<li>/get method Response (accountId) - <em>constant value</em>; Always set to &quot;self&quot;.</li>
<li>/get method Response (state) - <em>constant value</em>; Always set to empty string.</li>
<li>/get method Response (list) - <strong>required</strong></li>
<li>/get method Response (notFound) - <em>constant value</em>; Always set to empty array, see ids.</li>
</ul>
</section>

<section anchor="importing-data"><name>Importing data</name>
<t>The Session Object now additional requires:</t>

<ul spacing="compact">
<li>isReadOnly = false</li>
<li>maxObjectsInSet</li>
</ul>
<t>For methods:</t>

<ul spacing="compact">
<li>/set method Request - <strong>required</strong></li>
<li>/set method Request (accountId) - <em>constant value</em>; Is always &quot;self&quot;. Can therefore be ignored on the server side.</li>
<li>/set method Request (ifInState) - <em>constant value</em>; State is always empty string, so a simple check if this is empty string should suffice.</li>
<li>/set method Request (create, only single id) - <strong>required</strong></li>
<li>/set method Request (create, multiple ids) - not required; Only required if maxObjectsInSet &gt; 1.</li>
<li>/set method Request (update) - <em>error response</em>; Always place in notUpdated and reply with <tt>forbidden</tt> SetError. Its description property MUST explain that the update property is merely not supported.</li>
<li>/set method Request (destroy) - <em>error response</em>; Always place in notDestroyed and reply with <tt>forbidden</tt> SetError. Its description property MUST explain that the update property is merely not supported.</li>
<li>/set method Response - <strong>required</strong></li>
<li>/set method Response (accountId) - <em>constant value</em>; Is always &quot;self&quot;. Can therefore be ignored on the server side.</li>
<li>/set method Response (oldState) - <em>constant value</em>; Is always empty string.</li>
<li>/set method Response (newState) - <em>constant value</em>; Is always empty string.</li>
<li>/set method Response (created) - <strong>required</strong></li>
<li>/set method Response (updated) - <em>constant value</em>; Is always an empty array.</li>
<li>/set method Response (destroyed) - <em>constant value</em>; Is always empty array.</li>
<li>/set method Response (notCreated) - <strong>required</strong></li>
<li>/set method Response (notUpdated) - <em>error response</em>; In case update was not empty.</li>
<li>/set method Response (notDestroyed) - <em>error response</em>; In case destroy was not empty.</li>
<li>/set method SetError - <strong>required</strong>; Implement <tt>forbidden</tt> to signal update and destroy are not supported.</li>
</ul>
</section>
</section>

<section anchor="listing"><name>Listing</name>
<t>Methods:</t>

<ul spacing="compact">
<li>/get method Request (ids) - <strong>required</strong></li>
<li>/get method Response (notFound) - <strong>required</strong></li>
<li>/query method Request - <strong>required</strong></li>
<li>/query method Request (accountId) - <em>constant value</em>; Is always &quot;self&quot;. Can therefore be ignored on the server side.</li>
<li>/query method Request (filter) - <em>error response</em>; Always return <tt>unsupportedFilter</tt>.</li>
<li>/query method Request (sort) - <em>error response</em>; Always return <tt>unsupportedSort</tt>.</li>
<li>/query method Request (position) - <em>error response</em>; always return <tt>invalidArgument</tt>. Its description property MUST explain that the position property is merely not supported.</li>
<li>/query method Request (anchor) - <em>error response</em>; always return <tt>invalidArgument</tt>. Its description property MUST explain that the anchor property is merely not supported.</li>
<li>/query method Request (anchorOffset) - not required; This is because anchor is not supported.</li>
<li>/query method Request (limit) - <em>error response</em>; Always return <tt>invalidArgument</tt>. Its description property MUST explain that the limit property is merely not supported.</li>
<li>/query method Request (calculateTotal) - <em>error response</em>; Always return <tt>invalidArgument</tt>. Its description property MUST explain that the calculateTotal property is merely not supported.</li>
<li>/query method Response - <strong>required</strong></li>
<li>/query method Response (accountId) - <em>constant value</em>; Is always &quot;self&quot;. Can therefore be ignored on the server side.</li>
<li>/query method Response (queryState) - <strong>required</strong></li>
<li>/query method Response (canCalculateChanges) - <em>constant value</em>; Is always false.</li>
<li>/query method Response (position) - not required; position not supported</li>
<li>/query method Response (ids) - <strong>required</strong></li>
<li>/query method Response (total) - not required</li>
<li>/query method Response (limit) - <strong>required</strong></li>
<li>/query method FilterCondition - not required</li>
<li>/query method FilterOperator - not required</li>
<li>/query method Comparator - not required</li>
</ul>
</section>

<section anchor="paging"><name>Paging</name>
<t>Regarding methods, all requirements of <xref target="listing"></xref>, as well as:</t>

<ul spacing="compact">
<li>/query method Request (position) - <strong>required</strong></li>
<li>/query method Request (calculateTotal) - <strong>required</strong></li>
<li>/query method Response (position) - <strong>required</strong></li>
<li>/query method Response (total) - <strong>required</strong></li>
</ul>
</section>

<section anchor="dynamic-session-resource"><name>Dynamic Session Resource</name>

<section anchor="multi-account-scenario"><name>Multi-Account Scenario</name>
<t>A user might have access to more than one account. In this case, the constraints defined in <xref target="session-resource"></xref> no longer apply.</t>
<t>For the Session Object:</t>

<ul spacing="compact">
<li>choose how to determine accountId (e.g. base64-encode the username)</li>
<li>make sure that accounts returns more than a single account if a user has more</li>
</ul>
<t>Methods:</t>

<ul spacing="compact">
<li>method Request (accountId) - <strong>required</strong>; Can no loner be a constant value or ignored on the server side as a user might have multiple accounts.</li>
<li>method Response (accountId) - <strong>required</strong>; Can no longer be the constant value &quot;self&quot;.</li>
</ul>
</section>

<section anchor="varying-capabilities-or-server-configuration-between-users"><name>Varying Capabilities or Server configuration between users</name>
<t>Each user might have different restrictions regarding account capabilities. In this case, accounts will may differ from one user to another, and the constraints defined in <xref target="session-resource"></xref> no longer apply.</t>
<t>Other properties like uploadUrl, apiUrl etc. might change dynamically. It may no longer be possible to choose static values for them.</t>
</section>
</section>

<section anchor="attachments"><name>Attachments</name>

<section anchor="exporting-attachments"><name>Exporting attachments</name>
<t>For exporting data, choose for the Session Object:</t>

<ul spacing="compact">
<li>downloadUrl</li>
</ul>
<t>Implement:</t>

<ul spacing="compact">
<li>actual download endpoint</li>
</ul>
</section>

<section anchor="importing-attachments"><name>Importing attachments</name>
<t>For importing data, choose for the Session Object:</t>

<ul spacing="compact">
<li>uploadUrl</li>
<li>maxSizeUpload</li>
<li>maxConcurrentUpload</li>
</ul>
<t>Implement:</t>

<ul spacing="compact">
<li>actual upload endpoint</li>
</ul>
</section>
</section>
</section>

<section anchor="recommended-functionality"><name>Recommended functionality</name>
<t>Autodiscovery is useful, so clients can use the endpoint more easily.</t>
<t>Destroying objects via /set is very valuable functionality for testing. Without it, JMAP cannot be used to remove data. It requires the following method implementation:</t>

<ul spacing="compact">
<li>/set method Request (destroy) - <strong>required</strong></li>
<li>/set method Response (destroyed) - <strong>required</strong></li>
<li>/set method Response (notDestroyed) - <strong>required</strong></li>
</ul>
<t>The filter functionality of /query may be relevant for your use case. Filters allow listing objects of a specific kind.</t>
</section>
</section>

<section anchor="security-considerations"><name>Security considerations</name>
<t>All security considerations of JMAP <xref target="RFC8620"></xref> apply to this specification.</t>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document has no IANA actions.</t>
</section>

<section anchor="acknowledgements"><name>Acknowledgements</name>
<t>Bron Gondwana, Neil Jenkins, Alexey Melnikov, Ken Murchison, Robert Stepanek and
the JMAP working group at the IETF.</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8620.xml"/>
</references>

</back>

</rfc>
