idnits 2.17.1 draft-ietf-jmap-core-08.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** There are 4 instances of too long lines in the document, the longest one being 50 characters in excess of 72. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (September 10, 2018) is 2045 days in the past. Is this intentional? -- Found something which looks like a code comment -- if you have code sections in the document, please surround them with '' and '' lines. Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) -- Looks like a reference, but probably isn't: '1' on line 3219 ** Obsolete normative reference: RFC 5246 (Obsoleted by RFC 8446) ** Obsolete normative reference: RFC 5785 (Obsoleted by RFC 8615) ** Obsolete normative reference: RFC 7159 (Obsoleted by RFC 8259) ** Obsolete normative reference: RFC 7230 (Obsoleted by RFC 9110, RFC 9112) ** Obsolete normative reference: RFC 7235 (Obsoleted by RFC 9110) ** Obsolete normative reference: RFC 7807 (Obsoleted by RFC 9457) Summary: 7 errors (**), 0 flaws (~~), 1 warning (==), 3 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 JMAP N. Jenkins 3 Internet-Draft FastMail 4 Intended status: Standards Track C. Newman 5 Expires: March 14, 2019 Oracle 6 September 10, 2018 8 JSON Meta Application Protocol 9 draft-ietf-jmap-core-08 11 Abstract 13 This document specifies a protocol for clients to access JSON-based 14 data objects efficiently, with support for push and out-of-band 15 binary data upload/download. 17 Status of This Memo 19 This Internet-Draft is submitted in full conformance with the 20 provisions of BCP 78 and BCP 79. 22 Internet-Drafts are working documents of the Internet Engineering 23 Task Force (IETF). Note that other groups may also distribute 24 working documents as Internet-Drafts. The list of current Internet- 25 Drafts is at https://datatracker.ietf.org/drafts/current/. 27 Internet-Drafts are draft documents valid for a maximum of six months 28 and may be updated, replaced, or obsoleted by other documents at any 29 time. It is inappropriate to use Internet-Drafts as reference 30 material or to cite them other than as "work in progress." 32 This Internet-Draft will expire on March 14, 2019. 34 Copyright Notice 36 Copyright (c) 2018 IETF Trust and the persons identified as the 37 document authors. All rights reserved. 39 This document is subject to BCP 78 and the IETF Trust's Legal 40 Provisions Relating to IETF Documents 41 (https://trustee.ietf.org/license-info) in effect on the date of 42 publication of this document. Please review these documents 43 carefully, as they describe your rights and restrictions with respect 44 to this document. Code Components extracted from this document must 45 include Simplified BSD License text as described in Section 4.e of 46 the Trust Legal Provisions and are provided without warranty as 47 described in the Simplified BSD License. 49 Table of Contents 51 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 52 1.1. Notational conventions . . . . . . . . . . . . . . . . . 4 53 1.2. The Int and PositiveInt data types . . . . . . . . . . . 5 54 1.3. The Date and UTCDate data types . . . . . . . . . . . . . 5 55 1.4. JSON as the data encoding format . . . . . . . . . . . . 5 56 1.5. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5 57 1.5.1. User . . . . . . . . . . . . . . . . . . . . . . . . 5 58 1.5.2. Accounts . . . . . . . . . . . . . . . . . . . . . . 6 59 1.5.3. Data types and records . . . . . . . . . . . . . . . 6 60 1.6. Ids . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 61 1.7. The JMAP API model . . . . . . . . . . . . . . . . . . . 7 62 1.8. Vendor-specific extensions . . . . . . . . . . . . . . . 7 63 2. The JMAP Session resource . . . . . . . . . . . . . . . . . . 8 64 2.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 11 65 2.2. Service Autodiscovery . . . . . . . . . . . . . . . . . . 12 66 3. Structured data exchange . . . . . . . . . . . . . . . . . . 13 67 3.1. Making an API request . . . . . . . . . . . . . . . . . . 13 68 3.2. The Request object . . . . . . . . . . . . . . . . . . . 13 69 3.2.1. Example request . . . . . . . . . . . . . . . . . . . 14 70 3.3. The Response object . . . . . . . . . . . . . . . . . . . 15 71 3.3.1. Example response: . . . . . . . . . . . . . . . . . . 15 72 3.4. Omitting arguments . . . . . . . . . . . . . . . . . . . 16 73 3.5. Errors . . . . . . . . . . . . . . . . . . . . . . . . . 16 74 3.5.1. Request-level errors . . . . . . . . . . . . . . . . 16 75 3.5.2. Method-level errors . . . . . . . . . . . . . . . . . 17 76 3.6. References to previous method results . . . . . . . . . . 19 77 3.7. Security . . . . . . . . . . . . . . . . . . . . . . . . 23 78 3.8. Concurrency . . . . . . . . . . . . . . . . . . . . . . . 23 79 4. The Core/echo method . . . . . . . . . . . . . . . . . . . . 23 80 4.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 23 81 5. Standard methods and naming convention . . . . . . . . . . . 24 82 5.1. /get . . . . . . . . . . . . . . . . . . . . . . . . . . 24 83 5.2. /changes . . . . . . . . . . . . . . . . . . . . . . . . 25 84 5.3. /set . . . . . . . . . . . . . . . . . . . . . . . . . . 28 85 5.4. /copy . . . . . . . . . . . . . . . . . . . . . . . . . . 33 86 5.5. /query . . . . . . . . . . . . . . . . . . . . . . . . . 34 87 5.6. /queryChanges . . . . . . . . . . . . . . . . . . . . . . 39 88 5.7. Examples . . . . . . . . . . . . . . . . . . . . . . . . 41 89 5.8. Proxy considerations . . . . . . . . . . . . . . . . . . 47 90 6. Binary data . . . . . . . . . . . . . . . . . . . . . . . . . 48 91 6.1. Uploading binary data . . . . . . . . . . . . . . . . . . 49 92 6.2. Downloading binary data . . . . . . . . . . . . . . . . . 49 93 6.3. Blob/copy . . . . . . . . . . . . . . . . . . . . . . . . 50 94 7. Push . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 95 7.1. The StateChange object . . . . . . . . . . . . . . . . . 51 96 7.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 52 98 7.2. PushSubscription . . . . . . . . . . . . . . . . . . . . 52 99 7.2.1. PushSubscription/get . . . . . . . . . . . . . . . . 54 100 7.2.2. PushSubscription/set . . . . . . . . . . . . . . . . 54 101 7.2.3. Example . . . . . . . . . . . . . . . . . . . . . . . 55 102 7.3. Event Source . . . . . . . . . . . . . . . . . . . . . . 57 103 8. Security considerations . . . . . . . . . . . . . . . . . . . 58 104 8.1. Transport confidentiality . . . . . . . . . . . . . . . . 58 105 8.2. Authentication scheme . . . . . . . . . . . . . . . . . . 58 106 8.3. Service autodiscovery . . . . . . . . . . . . . . . . . . 58 107 8.4. JSON parsing . . . . . . . . . . . . . . . . . . . . . . 59 108 8.5. Denial of service . . . . . . . . . . . . . . . . . . . . 59 109 8.6. Push encryption . . . . . . . . . . . . . . . . . . . . . 59 110 9. IANA considerations . . . . . . . . . . . . . . . . . . . . . 59 111 9.1. Assignment of jmap service name . . . . . . . . . . . . . 59 112 9.2. Registration of well-known URI suffix for JMAP . . . . . 60 113 9.3. Registration of the jmap URN sub-namespace . . . . . . . 60 114 9.4. Creation of "JMAP Capabilities" registry . . . . . . . . 60 115 9.4.1. Preliminary community review . . . . . . . . . . . . 61 116 9.4.2. Submit request to IANA . . . . . . . . . . . . . . . 61 117 9.4.3. Designated expert review . . . . . . . . . . . . . . 61 118 9.4.4. Change procedures . . . . . . . . . . . . . . . . . . 62 119 9.4.5. JMAP Capabilities registry template: . . . . . . . . 62 120 9.4.6. Initial registration for JMAP core . . . . . . . . . 62 121 9.4.7. Registration for JMAP error placeholder in JMAP 122 capabilities registry . . . . . . . . . . . . . . . . 63 123 9.5. Creation of "JMAP Error Codes" registry . . . . . . . . . 63 124 9.5.1. Designated expert review . . . . . . . . . . . . . . 63 125 9.5.2. JMAP Error Codes registry template: . . . . . . . . . 64 126 9.5.3. Initial JMAP Error Codes registry . . . . . . . . . . 64 127 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 66 128 10.1. Normative References . . . . . . . . . . . . . . . . . . 66 129 10.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 69 130 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 69 132 1. Introduction 134 JMAP is a generic protocol for synchronising data, such as mail, 135 calendars or contacts, between a client and a server. It is 136 optimised for mobile and web environments, and aims to provide a 137 consistent interface to different data types. 139 This specification is for the generic mechanism of data 140 synchronisation. Further specifications define the data models for 141 different data types that may be synchronised via JMAP. 143 JMAP is designed to make efficient use of limited network resources. 144 Multiple API calls may be batched in a single request to the server, 145 reducing round trips and improving battery life on mobile devices. 147 Push connections remove the need for polling, and an efficient delta 148 update mechanism ensures a minimum of data is transferred. 150 JMAP is designed to be horizontally scalable to a very large number 151 of users. This is facilitated by the separate end points for users 152 after login, the separation of binary and structured data, and a 153 shared data model that does not allow data dependencies between 154 accounts. 156 1.1. Notational conventions 158 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 159 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 160 document are to be interpreted as described in [RFC2119]. 162 The underlying format used for this specification is JSON. 163 Consequently, the terms "object" and "array" as well as the four 164 primitive types (strings, numbers, booleans, and null) are to be 165 interpreted as described in Section 1 of [RFC7159]. Unless otherwise 166 noted, all the property names and values are case sensitive. 168 Some examples in this document contain "partial" JSON documents used 169 for illustrative purposes. In these examples, three periods "..." 170 are used to indicate a portion of the document that has been removed 171 for compactness. 173 Unless otherwise specified, examples of API exchanges only show the 174 _methodCalls_ array of the Request object or the _methodResponses_ 175 array of the Response object. For compactness, the rest of the 176 Request/Response object is omitted. 178 Type signatures are given for all JSON values in this document. The 179 following conventions are used: 181 o "*" - The type is undefined (the value could be any type, although 182 permitted values may be constrained by the context of this value). 184 o "String" - The JSON string type. 186 o "Number" - The JSON number type. 188 o "Boolean" - The JSON boolean type. 190 o "String[A]" - A JSON object where the keys are all "String"s, and 191 the values are of type "A". 193 o "A[]" - An array of values of type "A". 195 o "A|B" - The value is either of type "A" or of type "B". 197 Other types may also be given, with their representation defined 198 elsewhere in this document. 200 1.2. The Int and PositiveInt data types 202 Where "Int" is given as a data type, it means an integer in the range 203 -2^53 <= value <= 2^53 (the maximum integer that may be reliably 204 stored in a floating-point double), represented as a JSON "Number". 206 Where "PositiveInt" is given as a data type, it means an "Int" where 207 the value MUST be in the range 0 <= value <= 2^53. 209 1.3. The Date and UTCDate data types 211 Where "Date" is given as a type, it means a string in [RFC3339] 212 _date-time_ format. To ensure a normalised form, the _time-secfrac_ 213 MUST always be omitted and any letters in the string (e.g. "T" and 214 "Z") MUST be upper-case. For example, ""2014-10-30T14:12:00+08:00"". 216 Where "UTCDate" is given as a type, it means a "Date" where the 217 _time-offset_ component MUST be "Z" (i.e. it must be in UTC time). 218 For example, ""2014-10-30T06:12:00Z"". 220 1.4. JSON as the data encoding format 222 JSON is a text-based data interchange format as specified in 223 [RFC7159]. The I-JSON format defined in [RFC7493] is a strict subset 224 of this, adding restrictions to avoid potentially confusing scenarios 225 (for example, it mandates that an object MUST NOT have two properties 226 with the same key). 228 All data sent from the client to the server or from the server to the 229 client (except binary file upload/download) MUST be valid I-JSON 230 according to the RFC, and is therefore case-sensitive and encoded in 231 UTF-8 ([RFC3629]). 233 1.5. Terminology 235 1.5.1. User 237 A user represents a set of permissions relating to what data can be 238 seen. 240 1.5.2. Accounts 242 An account is a collection of data. A single account may contain an 243 arbitrary set of data types, for example a collection of mail, 244 contacts and calendars. 246 All data belongs to a single account. With the exception of a few 247 explicit operations to copy data between accounts, all JMAP methods 248 take an _accountId_ argument that specifies on which account the 249 operations are to take place. This argument is always optional; if 250 not specified, the primary account for the capability that defines 251 the data type is used. (Though if there is no primary account for 252 that capability, an "accountNotFound" error will be returned.) 254 An account is not the same as a user, although it is common for a 255 primary account to directly belong to the user. For example, you may 256 have an account that contains data for a group or business, to which 257 multiple users have access. 259 A single set of credentials may provide access to multiple accounts, 260 for example if another user is sharing their mail with the logged in 261 user, or if there is a group account. 263 In the event of a severe internal error, a server may have to 264 reallocate ids or do something else that violates standard JMAP data 265 constraints for an account. In this situation, the data on the 266 server is no longer compatible with cached data the client may have 267 from before. The server MUST treat this as though the account has 268 been deleted and then recreated with a new account id. Clients will 269 then be forced to throw away any data with the old account id and 270 refetch all data from scratch. 272 1.5.3. Data types and records 274 JMAP provides a uniform interface for creating, retrieving, updating 275 and deleting various types of objects. A *data type* is a collection 276 of named, typed properties, just like the schema for a database 277 table. Each instance of a data type is called a *record*. 279 1.6. Ids 281 All record ids are assigned by the server, and are immutable. They 282 MUST be unique among all records of the *same type* within the *same 283 account*. Ids may clash across accounts, or for two records of 284 different types within the same account. 286 Ids are always "String"s. An id MUST be at least 1 character in 287 length and maximum 255 octets in size, and MUST only contain 288 characters from the "URL and Filename safe" Base 64 Alphabet, as 289 defined in section 5 of [RFC4648]. This is the ASCII alphanumeric 290 characters ("A-Za-z0-9"), hyphen ("-"), and underscore ("_"). 292 1.7. The JMAP API model 294 JMAP uses HTTP [RFC7230] to expose API, Push, Upload and Download 295 resources. Implementations MUST support HTTP/1.1, and MAY support 296 later versions. All HTTP requests MUST use [RFC5246] TLS (HTTPS) 297 transport. Support for common HTTP mechanisms such as redirection 298 and caching are assumed. 300 All HTTP requests MUST be authenticated. Servers MUST conform with 301 the [RFC7235] HTTP Authentication framework to reject requests that 302 fail authentication and inform the client of available authentication 303 schemes. 305 Clients SHOULD understand and be able to handle standard HTTP status 306 codes appropriately. 308 An authenticated client can fetch the JMAP Session object with 309 details about the data and capabilities the server can provide as 310 shown in section 2. The client may then exchange data with the 311 server in the following ways: 313 1. The client may make an API request to the server to get or set 314 structured data. This request consists of an ordered series of 315 method calls. These are processed by the server, which then 316 returns an ordered series of responses. This is described in 317 sections 3 and 4. 319 2. The client may download or upload binary files from/to the 320 server. This is detailed in section 5. 322 3. The client may connect to a push channel on the server, to be 323 notified when data has changed. This is explained in section 6. 325 1.8. Vendor-specific extensions 327 Individual services will have custom features they wish to expose 328 over JMAP. This may take the form of extra data types and/or methods 329 not in the spec, or extra arguments to JMAP methods, or extra 330 properties on existing data types (which may also appear in arguments 331 to methods that take property names). 333 The server can advertise custom extensions it supports by including 334 the identifiers in the capabilities object. Identifiers for vendor 335 extensions MUST be a URL belonging to a domain owned by the vendor, 336 to avoid conflict. The URL SHOULD resolve to documentation for the 337 changes the extension makes. 339 To ensure compatibility with clients that don't know about a specific 340 custom extension, and for compatibility with future versions of JMAP, 341 to use an extension the client MUST opt in by passing the appropriate 342 capability identifier in the _using_ array of the Request object, as 343 described in section 3.2. The server MUST only follow the 344 specifications that are opted-into and behave as though it does not 345 implement anything else when processing a request. 347 2. The JMAP Session resource 349 You need two things to connect to a JMAP server: 351 1. The URL for the JMAP Session resource. This may be requested 352 directly from the user, or discovered automatically based on a 353 username domain (see Service Autodiscovery section below). 355 2. Credentials to authenticate with. How to obtain credentials is 356 out of scope for this specification. 358 An authenticated GET request to the JMAP Session resource MUST return 359 the details about the data and capabilities the server can provide to 360 the client given those credentials. 362 The response to a successful request is a JSON-encoded *JMAP Session* 363 object. It has the following properties: 365 o *username*: "String" The username associated with the given 366 credentials. 368 o *accounts*: "String[Account]" A map of *account id* to Account 369 object for each account (see section 1.5.2) the user has access 370 to. An *Account* object has the following properties: 372 * *name*: "String" A user-friendly string to show when presenting 373 content from this account, e.g. the email address representing 374 the owner of the account. 376 * *isPersonal*: "Boolean" This is "true" if the account belongs 377 to the authenticated user, rather than a group account or a 378 personal account of another user that has been shared with 379 them. 381 * *isReadOnly*: "Boolean" This is "true" if the entire account is 382 read-only. 384 * *hasDataFor*: "String[]" A list of specification URIs for the 385 object types supported in this account. The server advertises 386 the list of specifications it supports in general in the 387 capabilities object, as defined below. If the specification 388 includes new object type definitions, the server MUST include 389 it the _hasDataFor_ array if, and only if, the user may use 390 those data types with this account. For example, you may have 391 access to your own account with mail, calendars and contacts 392 data, and also a shared account that only has contacts data (a 393 business address book for example). In this case the 394 _hasDataFor_ property on the first account would include 395 something like "urn:ietf:params:jmap:mail", 396 "urn:ietf:params:jmap:calendars", 397 "urn:ietf:params:jmap:contacts", while the second account would 398 just have the last of these. Attempts to use the methods 399 defined in a specification with one of the accounts that does 400 not contain those data types are rejected with an 401 _accountNotSupportedByMethod_ error (see the Method-level 402 errors section below). 404 o *primaryAccounts*: "String[String]" A map of capability URIs (as 405 found in _hasDataFor_) to the account id to be considered the 406 user's main or default account for data pertaining to that 407 capability. If no account being returned belongs to the user, or 408 in any other way there is no appropriate way to determine a 409 default account, there MAY be no entry for a particular data 410 profile name. "urn:ietf:params:jmap:core" SHOULD NOT be present. 412 o *capabilities*: "String[Object]" An object specifying the 413 capabilities of this server. Each key is a URI for a 414 specification supported by the server. The value for each of 415 these keys is an object with further information about the 416 server's capabilities in relation to that specification. The 417 client MUST ignore any properties it does not understand. The 418 capabilities object MUST include a property called 419 "urn:ietf:params:jmap:core". The value of this property is an 420 object which MUST contain the following information on server 421 capabilities: 423 * *maxSizeUpload*: "PositiveInt" The maximum file size, in 424 octets, that the server will accept for a single file upload 425 (for any purpose). 427 * *maxConcurrentUpload*: "PositiveInt" The maximum number of 428 concurrent requests the server will accept to the upload 429 endpoint. 431 * *maxSizeRequest*: "PositiveInt" The maximum size, in octets, 432 that the server will accept for a single request to the API 433 endpoint. 435 * *maxConcurrentRequests*: "PositiveInt" The maximum number of 436 concurrent requests the server will accept to the API endpoint. 438 * *maxCallsInRequest*: "PositiveInt" The maximum number of method 439 calls the server will accept in a single request to the API 440 endpoint. This MUST be greater than or equal to "32" to ensure 441 clients can rely on the ability to make efficient network use. 443 * *maxObjectsInGet*: "PositiveInt" The maximum number of objects 444 that the client may request in a single "/get" type method 445 call. 447 * *maxObjectsInSet*: "PositiveInt" The maximum number of objects 448 the client may send to create, update or destroy in a single 449 "/set" type method call. 451 * *collationAlgorithms*: "String[]" A list of identifiers for 452 algorithms registered in the collation registry defined in 453 [RFC4790] that the server supports for sorting when querying 454 records. 456 Future specifications will define their own properties on the 457 capabilities object. Servers MAY advertise vendor-specific JMAP 458 extensions, as described in section 1.8. To avoid conflict, the 459 identifiers for these MUST be a URL with a domain owned by the 460 vendor. Clients MUST opt in to any specifications it wishes to 461 use (see section 3.1). 463 o *apiUrl*: "String" The URL to use for JMAP API requests. 465 o *downloadUrl*: "String" The URL endpoint to use when downloading 466 files in [RFC6570] URI Template (level 1) format. The URL MUST 467 contain variables called "accountId", "blobId", "type" and "name". 468 The use of these variables is described in section 5.2. Due to 469 potential encoding issues with slashes in content types, it is 470 recommended to put the "type" variable in the query section of the 471 URL. 473 o *uploadUrl*: "String" The URL endpoint to use when uploading files 474 in [RFC6570] URI Template (level 1) format. The URL MUST contain 475 a variable called "accountId". The use of this variable is 476 described in section 5.1. 478 o *eventSourceUrl*: "String" The URL to connect to for push events, 479 as described in section 6.3. 481 o *state*: "String" A string representing the state of this object 482 on the server. If the value of any other property on the session 483 object changes, this string will change. The current value is 484 also returned on the API Response object, allowing clients to 485 quickly determine if the session information has changed (e.g. an 486 account has been added or removed) and so they need to refetch the 487 object. 489 To ensure future compatibility, other properties MAY be included on 490 the JMAP Session object. Clients MUST ignore any properties they are 491 not expecting. 493 Implementors must take care to avoid inappropriate caching of the 494 session object at the HTTP layer. Since the client should only 495 refetch when it detects there is a change (via the sessionState 496 property of an API response), it is recommended to disable HTTP 497 caching altogether, for example by setting "Cache-Control: no-cache, 498 no-store, must-revalidate" on the response. 500 2.1. Example 502 In the following example JMAP Session object, the user has access to 503 his own mail and contacts via JMAP, as well as read-only access to 504 shared mail from another user. The server is advertising a custom 505 "https://example.com/apis/foobar" capability. 507 { 508 "username": "john@example.com", 509 "accounts": { 510 "13824": { 511 "name": "john@example.com", 512 "isPersonal": true, 513 "isReadOnly": false, 514 "hasDataFor": [ 515 "urn:ietf:params:jmap:mail", 516 "urn:ietf:params:jmap:contacts" 517 ] 518 }, 519 "97813": { 520 "name": "jane@example.com", 521 "isPersonal": false, 522 "isReadOnly": true, 523 "hasDataFor": [ "urn:ietf:params:jmap:mail" ] 524 } 525 }, 526 "primaryAccounts": { 527 "urn:ietf:params:jmap:mail": "13824", 528 "urn:ietf:params:jmap:contacts": "13824" 529 }, 530 "capabilities": { 531 "urn:ietf:params:jmap:core": { 532 "maxSizeUpload": 50000000, 533 "maxConcurrentUpload": 8, 534 "maxSizeRequest": 10000000, 535 "maxConcurrentRequest": 8, 536 "maxCallsInRequest": 32, 537 "maxObjectsInGet": 256, 538 "maxObjectsInSet": 128, 539 "collationAlgorithms": [ 540 "i;ascii-numeric", 541 "i;ascii-casemap", 542 "i;unicode-casemap" 543 ] 544 }, 545 "https://example.com/apis/foobar": { 546 "maxFoosFinangled": 42 547 }, 548 ... 549 }, 550 "apiUrl": "https://jmap.example.com/api/", 551 "downloadUrl": "https://jmap.example.com/download/{accountId}/{blobId}/{name}?accept={type}", 552 "uploadUrl": "https://jmap.example.com/upload/{accountId}/", 553 "eventSourceUrl": "https://jmap.example.com/eventsource/", 554 "state": "75128aab4b1b" 555 } 557 2.2. Service Autodiscovery 559 There are two standardised autodiscovery methods in use for internet 560 protocols: 562 o *DNS SRV* ([RFC2782], [RFC6186] and [RFC6764]) 564 o *.well-known/servicename* ([RFC5785]) 566 A JMAP-supporting host for the domain "example.com" SHOULD publish a 567 SRV record "_jmap._tcp.example.com" which gives a _hostname_ and 568 _port_ (usually port "443"). The JMAP Session resource is then 569 "https://${hostname}[:${port}]/.well-known/jmap" (following any 570 redirects). 572 If the client has a username in the form of an email address, it MAY 573 use the domain portion of this to attempt autodiscovery of the JMAP 574 server. 576 3. Structured data exchange 578 The client may make an API request to the server to get or set 579 structured data. This request consists of an ordered series of 580 method calls. These are processed by the server, which then returns 581 an ordered series of responses. 583 3.1. Making an API request 585 To make an API request, the client makes an authenticated POST 586 request to the API resource, which is defined by the _apiUrl_ 587 property in the JMAP Session object. 589 The request MUST be of type "application/json" and consist of a 590 single JSON *Request* object. If successful, the response MUST also 591 be of type "application/json" and consist of a single *Response* 592 object. 594 3.2. The Request object 596 A *Request* object has the following properties: 598 o *using*: "String[]" The set of capabilities the client wishes to 599 use. The client MAY include capability identifiers even if the 600 method calls it makes do not utilise those capabilities. The 601 server advertises the set of specifications it supports in the 602 JMAP Session object, as keys on the _capabilities_ property. 604 o *methodCalls*: "Invocation[]" An array of method calls to process 605 on the server. The method calls MUST be processed sequentially, 606 in order. An *Invocation* is a tuple, represented as a JSON array 607 containing three elements: 609 1. A "String" *name* of the method to call. 611 2. A "String[*]" object containing _named_ *arguments* for that 612 method. 614 3. A "String" *client id*: an arbitrary string from the client to 615 be echoed back with the responses emitted by that method call 616 (a method may return 1 or more responses, as it may make 617 implicit calls to other methods; all responses initiated by 618 this method call get the same client id in the response). 620 o *createdIds*: "String[String]" (optional) A map of (client- 621 specified) creation id to the id the server assigned when a record 622 was successfully created. As described later in this 623 specification, some records may have a property that contains the 624 id of another record. To allow more efficient network usage, you 625 can set this property to reference a record created earlier in the 626 same API request. Since the real id is unknown when the request 627 is created, the client can instead specify the creation id it 628 assigned, prefixed with a "#". As the server processes API 629 requests, any time it successfully creates a new record it adds to 630 this map the creation id, with the server-assigned real id as the 631 value. If it comes across a reference to a creation id in a 632 create/update, it looks it up in the map and replaces the 633 reference with the real id, if found. The client can pass an 634 initial value for this map as the _createdIds_ property of the 635 Request. This may be an empty object. If given in the request, 636 the response will also include a createdIds property, with any 637 additionally created ids added. This allows proxy servers to 638 easily split a JMAP request into multiple JMAP requests to send to 639 different servers. For example it could send the first two method 640 calls to server A, then the third to server B, before sending the 641 fourth to server A again. By passing the createdIds of the 642 previous response to the next request, it can ensure all of these 643 still resolve. 645 Future specifications MAY add further properties to the Request 646 object to extend the semantics. To ensure forwards compatibility, a 647 server MUST ignore any other properties it does not understand on the 648 JMAP request object. 650 3.2.1. Example request 652 { 653 "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], 654 "methodCalls": [ 655 [ "method1", { 656 "arg1": "arg1data", 657 "arg2": "arg2data" 658 }, "c1" ], 659 [ "method2", { 660 "arg1": "arg1data" 661 }, "c2" ], 662 [ "method3", {}, "c3" ] 663 ] 664 } 665 3.3. The Response object 667 A *Response* object has the following properties: 669 o *methodResponses*: "Invocation[]" An array of responses, in the 670 same format as the _methodCalls_ on the request object. The 671 output of the methods MUST be added to the _methodResponses_ array 672 in the same order as the methods are processed. 674 o *createdIds*: "String[String]" (optional; only returned if given 675 in request) A map of (client-specified) creation id to the id the 676 server assigned when a record was successfully created. This 677 includes all values passed in the request, as well as any 678 additional ones added for newly created records. 680 o *sessionState*: "String" The current value of the "state" string 681 on the JMAP Session object. Clients may use this to detect if 682 this object has changed and needs to be refetched. 684 Unless otherwise specified, if the method call completed successfully 685 its response name is the same as the method name in the request. 687 3.3.1. Example response: 689 { 690 "methodResponses": [ 691 [ "method1", { 692 "arg1": 3, 693 "arg2": "foo" 694 }, "c1" ], 695 [ "method2", { 696 "isBlah": true 697 }, "c2" ], 698 [ "anotherResponseFromMethod2", { 699 "data": 10, 700 "yetmoredata": "Hello" 701 }, "c2"], 702 [ "error", { 703 "type":"unknownMethod" 704 }, "c3" ] 705 ], 706 "sessionState": "75128aab4b1b" 707 } 709 3.4. Omitting arguments 711 An argument to a method may be specified to have a default value. If 712 omitted by the client, the server MUST treat the method call the same 713 as if the default value had been specified. Similarly, the server 714 MAY omit any argument in a response which has the default value. 716 Unless otherwise specified in a method description, "null" is the 717 default value for any argument in a request or response where this is 718 allowed by the type signature. Other arguments may only be omitted 719 if an explicit default value is defined in the method description. 721 3.5. Errors 723 There are three different levels of granularity at which an error may 724 be returned in JMAP. 726 When an API request is made, the request as a whole may be rejected 727 due to rate limiting, malformed JSON, request for an unknown 728 capability etc. In this case the entire request is rejected with an 729 appropriate HTTP error response code, and an additional JSON body 730 with more detail for the client. 732 Provided the request itself is syntactically valid, the methods 733 within it are executed sequentially by the server. Each method may 734 individually fail, for example if invalid arguments are given, or an 735 unknown method name is called. 737 Finally, methods that make changes to the server state often act upon 738 a number of different records within a single call. Each record 739 change may be separately rejected with a SetError, as described in 740 section 5.3. 742 3.5.1. Request-level errors 744 When an HTTP error response is returned to the client, the server 745 SHOULD return a JSON "problem details" object as the response body, 746 as per [RFC7807]. 748 The following problem types are defined: 750 o "urn:ietf:params:jmap:error:unknownCapability" The client included 751 a capability in the "using" property of the request that the 752 server does not support. 754 o "urn:ietf:params:jmap:error:notJSON" The content type of the 755 request was not "application/json" or the request did not parse as 756 I-JSON. 758 o "urn:ietf:params:jmap:error:notRequest" The request parsed as JSON 759 but did not match the structure of the Request object. 761 o "urn:ietf:params:jmap:error:limit" The request was not processed 762 as it would have exceeded one of the *request* limits defined on 763 the capability object, such as maxSizeRequest, maxCallsInRequest 764 or maxConcurrentRequests. A "limit" property MUST also be present 765 on the "problem details" object, containing the name of the limit 766 being applied. 768 3.5.1.1. Example 770 { 771 "type": "urn:ietf:params:jmap:error:unknownCapability", 772 "status": 400, 773 "detail": "The request object used capability 'https://example.com/apis/foobar', which is not supported by this server." 774 } 776 3.5.2. Method-level errors 778 If a method encounters an error, the appropriate "error" response 779 MUST be inserted at the current point in the _methodResponses_ array 780 and, unless otherwise specified, further processing MUST NOT happen 781 within that method call. 783 Any further method calls in the request MUST then be processed as 784 normal. Errors at the method level MUST NOT generate an HTTP-level 785 error. 787 An "error" response looks like this: 789 [ "error", { 790 "type": "unknownMethod" 791 }, "client-id" ] 793 The response name is "error", and it MUST have a type property. 794 Other properties may be present with further information; these are 795 detailed in the error type descriptions where appropriate. 797 With the exception of "serverPartialFail", the externally-visible 798 state of the server MUST NOT have changed if an error is returned at 799 the method level. 801 The following error types are defined which may be returned for any 802 method call where appropriate: 804 "serverUnavailable": Some internal server resource was temporarily 805 unavailable. Attempting the same operation later (perhaps after a 806 backoff with a random factor) may succeed. 808 "serverFail": An unexpected or unknown error occurred during the 809 processing of the call. A _description_ property should provide more 810 details about the error. The method call made no changes to the 811 server's state. Attempting the same operation again is expected to 812 fail again. Contacting the service administrator is likely necessary 813 to resolve this problem if it is persistent. 815 "serverPartialFail": Some, but not all expected changes described by 816 the method occurred. The client MUST re-synchronise impacted data to 817 determine server state. Use of this error is strongly discouraged. 819 "unknownMethod": The server does not recognise this method name. 821 "invalidArguments": One of the arguments is of the wrong type or 822 otherwise invalid, or a required argument is missing. A 823 "description" property MAY be present to help debug with an 824 explanation of what the problem was. This is a non-localised string, 825 and is not intended to be shown directly to end users. 827 "invalidResultReference": The method used a back reference for one of 828 its arguments (see the next section), but this failed to resolve. 830 "forbidden": The method and arguments are valid, but executing the 831 method would violate an ACL or other permissions policy. 833 "accountNotFound": The _accountId_ does not correspond to a valid 834 account. 836 "accountNotSupportedByMethod": The _accountId_ given corresponds to a 837 valid account, but the account does not support this data type. 839 "accountReadOnly": This method call would modify state in an account 840 that has "isReadOnly == true". 842 Further possible errors for a particular method are specified in the 843 method descriptions. 845 Further general errors MAY be defined in future RFCs. Should a 846 client receive an error type it does not understand, it MUST treat it 847 the same as the "serverFail" type. 849 3.6. References to previous method results 851 To allow clients to make more efficient use of the network and avoid 852 round trips, an argument to one method can be taken from the result 853 of a previous method call. 855 To do this, the client prefixes the argument name with "#". The 856 value is a _ResultReference_ object as described below. When 857 processing a method call, the server MUST first check the arguments 858 object for any names beginning with "#". If found, the back 859 reference should be resolved and the value used as the "real" 860 argument. The method is then processed as normal. If any back 861 reference fails to resolve, the whole method MUST be rejected with an 862 "invalidResultReference" error. If an argument object contains the 863 same argument name in normal and referenced form (e.g. "foo" and 864 "#foo"), the method MUST return an "invalidArguments" error. 866 A *ResultReference* object has the following properties: 868 o *resultOf*: "String" The client id of the method call to get the 869 result from (the string given as the third item in the array for a 870 method call). 872 o *name*: "String" The expected name of the response. 874 o *path*: "String" A pointer into the arguments. This is an 875 [RFC6901] JSON Pointer, except it also allows the use of "*" to 876 map through an array (see description below). 878 To resolve: 880 1. Find the first response with a client id identical to the 881 _resultOf_ property of the _ResultReference_ in the 882 _methodResponses_ array from previously processed method calls in 883 the same request. If none, evaluation fails. 885 2. If the response name is not identical to the _name_ property of 886 the _ResultReference_, evaluation fails. 888 3. Apply the _path_ to the arguments object of the response (the 889 second item in the response array) following the [RFC6901] JSON 890 Pointer algorithm, except with the following addition in 891 Section 4 (Evaluation): 893 If the currently referenced value is a JSON array, the reference 894 token may be exactly the single character "*", making the new 895 referenced value the result of applying the rest of the JSON pointer 896 tokens to every item in the array and returning the results in the 897 same order in a new array. If the result of applying the rest of the 898 pointer tokens to a value was itself an array, its items should be 899 included individually in the output rather than including the array 900 itself (i.e. the result is flattened from an array of arrays to a 901 single array). 903 As a simple example, suppose we have the following API request 904 _methodCalls_: 906 [[ "Foo/changes", { 907 "accountId": "1", 908 "sinceState": "abcdef" 909 }, "t0" ], 910 [ "Foo/get", { 911 "accountId": "1", 912 "#ids": { 913 "resultOf": "t0", 914 "name": "Foo/changes", 915 "path": "/created" 916 } 917 }, "t1" ]] 919 After executing the first method call the _methodResponses_ array is: 921 [[ "Foo/changes", { 922 "accountId": "1", 923 "oldState": "abcdef", 924 "newState": "123456", 925 "hasMoreChanges": false, 926 "created": [ "f1", "f4" ], 927 "updated": [], 928 "destroyed": [] 929 }, "t0" ]] 931 So to execute the Foo/get call, we look through the arguments and 932 find there is one with a "#" prefix. To resolve this, we apply the 933 algorithm above: 935 1. Find the first response with client id "t0". The Foo/changes 936 response fulfils this criterion. 938 2. Check the response name is the same as in the result reference. 939 It is, so this is fine. 941 3. Apply the _path_ as a JSON pointer to the arguments object. This 942 simply selects the "created" property, so the result of 943 evaluating is: "[ "f1", "f4" ]" 945 The JMAP server now continues to process the Foo/get call as though 946 the arguments were: 948 { 949 "ids": [ "f1", "f4" ] 950 } 952 Now a more complicated example using the JMAP Mail data model: fetch 953 the "from"/"date"/"subject" for every email in the first 10 threads 954 in the Inbox (sorted newest first): 956 [[ "Email/query", { 957 "accountId": "1", 958 "filter": { "inMailbox": "id_of_inbox" }, 959 "sort": [{ "property": "receivedAt", "isAscending": false }], 960 "collapseThreads": true, 961 "position": 0, 962 "limit": 10, 963 "calculateTotal": true 964 }, "t0" ], 965 [ "Email/get", { 966 "accountId": "1", 967 "#ids": { 968 "resultOf": "t0", 969 "name": "Email/query", 970 "path": "/ids" 971 }, 972 "properties": [ "threadId" ] 973 }, "t1" ], 974 [ "Thread/get", { 975 "accountId": "1", 976 "#ids": { 977 "resultOf": "t1", 978 "name": "Email/get", 979 "path": "/list/*/threadId" 980 } 981 }, "t2" ], 982 [ "Email/get", { 983 "accountId": "1", 984 "#ids": { 985 "resultOf": "t2", 986 "name": "Thread/get", 987 "path": "/list/*/emailIds" 988 }, 989 "properties": [ "from", "receivedAt", "subject" ] 990 }, "t3" ]] 992 After executing the first 3 method calls the _methodResponses_ array 993 might be: 995 [[ "Email/query", { 996 "accountId": "1", 997 "filter": { "inMailbox": "id_of_inbox" }, 998 "sort": [{ "property": "receivedAt", "isAscending": false }], 999 "collapseThreads": true, 1000 "queryState": "abcdefg", 1001 "canCalculateChanges": true, 1002 "position": 0, 1003 "total": 101, 1004 "ids": [ "msg1023", "msg223", "msg110", "msg93", "msg91", "msg38", "msg36", "msg33", "msg11", "msg1" ] 1005 }, "t0" ], 1006 [ "Email/get", { 1007 "accountId": "1", 1008 "state": "123456", 1009 "list": [{ 1010 "id": "msg1023", 1011 "threadId": "trd194", 1012 }, { 1013 "id": "msg223", 1014 "threadId": "trd114" 1015 }, 1016 ... 1017 ], 1018 "notFound": [] 1019 }, "t1" ], 1020 [ "Thread/get", { 1021 "accountId": "1", 1022 "state": "123456", 1023 "list": [{ 1024 "id: "trd194", 1025 "emailIds": [ "msg1020", "msg1021", "msg1023" ] 1026 }, { 1027 "id: "trd114", 1028 "emailIds": [ "msg201", "msg223" ] 1029 }, 1030 ... 1031 ], 1032 "notFound": [] 1033 }, "t2" ]] 1035 So to execute the final Email/get call, we look through the arguments 1036 and find there is one with a "#" prefix. To resolve this, we apply 1037 the algorithm: 1039 1. Find the first response with client id "t2". The "Thread/get" 1040 response fulfils this criterion. 1042 2. "Thread/get" is the name specified in the result reference, so 1043 this is fine. 1045 3. Apply the _path_ as a JSON pointer to the arguments object. 1046 Token-by-token: a) "list": get the array of thread objects b) 1047 "*": for each of the items in the array: 1049 i) `emailIds`: get the array of email ids 1050 ii) Concatenate these into a single array of all the ids in the result. 1052 The JMAP server now continues to process the Email/get call as though 1053 the arguments were: 1055 { 1056 "ids": [ "msg1020", "msg1021", "msg1023", "msg201", "msg223", ... ], 1057 "properties": [ "from", "receivedAt", "subject" ] 1058 } 1060 3.7. Security 1062 As always, the server must be strict about data received from the 1063 client. Arguments need to be checked for validity; a malicious user 1064 could attempt to find an exploit through the API. In case of invalid 1065 arguments (unknown/insufficient/wrong type for data etc.) the method 1066 MUST return an "invalidArguments" error and terminate. 1068 3.8. Concurrency 1070 Method calls within a single request MUST be executed in order. 1071 However, method calls from different concurrent API requests may be 1072 interleaved. This means that the data on the server may change 1073 between two method calls within a single API request. 1075 4. The Core/echo method 1077 The _Core/echo_ method returns exactly the same arguments as it is 1078 given. It is useful for testing you have a valid authenticated 1079 connection to a JMAP API endpoint. 1081 4.1. Example 1083 Request: 1085 [[ "Core/echo", { 1086 "hello": true, 1087 "high": 5 1088 }, "b3ff" ]] 1090 Response: 1092 [[ "Core/echo", { 1093 "hello": true, 1094 "high": 5 1095 }, "b3ff" ]] 1097 5. Standard methods and naming convention 1099 JMAP provides a uniform interface for creating, retrieving, updating 1100 and deleting objects of a particular type. For a "Foo" data type, 1101 records of that type would be fetched via a Foo/get call and modified 1102 via a Foo/set call. Delta updates may be fetched via a Foo/changes 1103 call. These methods all follow a standard format as described below. 1105 Not all types may have all methods. Specifications defining types 1106 MUST specify which methods are available for the type. 1108 5.1. /get 1110 Objects of type *Foo* are fetched via a call to _Foo/get_. 1112 It takes the following arguments: 1114 o *accountId*: "String" The id of the account to use. 1116 o *ids*: "String[]|null" The ids of the Foo objects to return. If 1117 "null" then *all* records of the data type are returned, if this 1118 is supported for that data type. 1120 o *properties*: "String[]|null" If supplied, only the properties 1121 listed in the array are returned for each Foo object. If "null", 1122 all properties of the object are returned. The id of the object 1123 is *always* returned, even if not explicitly requested. If an 1124 invalid property is requested, the call MUST be rejected with an 1125 "invalidArguments" error. 1127 The response has the following arguments: 1129 o *accountId*: "String" The id of the account used for the call. 1131 o *state*: "String" A string representing the state on the server 1132 for *all* the data of this type in the account (not just the 1133 objects returned in this call). If the data changes, this string 1134 MUST change. If the Foo data is unchanged, servers SHOULD return 1135 the same state string on subsequent requests for this data type. 1136 When a client receives a response with a different state string to 1137 a previous call, it MUST either throw away all currently cached 1138 objects for the type, or call _Foo/changes_ to get the exact 1139 changes. 1141 o *list*: "Foo[]" An array of the Foo objects requested. This is 1142 the *empty array* if no objects were found, or if the _ids_ 1143 argument passed in was also the empty array. The results MAY be 1144 in a different order to the _ids_ in the request arguments. If an 1145 identical id is included more than once in the request, the server 1146 MUST only include it once in either the _list_ or _notFound_ 1147 argument of the response. 1149 o *notFound*: "String[]" This array contains the ids passed to the 1150 method for records that do not exist. The array is empty if all 1151 requested ids were found, or if the _ids_ argument passed in was 1152 either "null" or the empty array. 1154 The following additional error may be returned instead of the _Foo/ 1155 get_ response: 1157 "requestTooLarge": The number of _ids_ requested by the client 1158 exceeds the maximum number the server is willing to process in a 1159 single method call. 1161 5.2. /changes 1163 When the state of the set of Foo records changes on the server 1164 (whether due to creation, updates or deletion), the _state_ property 1165 of the _Foo/get_ response will change. The _Foo/changes_ method 1166 allows a client to efficiently update the state of its Foo cache to 1167 match the new state on the server. It takes the following arguments: 1169 o *accountId*: "String" The id of the account to use. 1171 o *sinceState*: "String" The current state of the client. This is 1172 the string that was returned as the _state_ argument in the _Foo/ 1173 get_ response. The server will return the changes that have 1174 occurred since this state. 1176 o *maxChanges*: "PositiveInt|null" The maximum number of ids to 1177 return in the response. The server MAY choose to return fewer 1178 than this value, but MUST NOT return more. If not given by the 1179 client, the server may choose how many to return. If supplied by 1180 the client, the value MUST be a positive integer greater than 0. 1182 If a value outside of this range is given, the server MUST reject 1183 the call with an "invalidArguments" error. 1185 The response has the following arguments: 1187 o *accountId*: "String" The id of the account used for the call. 1189 o *oldState*: "String" This is the _sinceState_ argument echoed 1190 back; the state from which the server is returning changes. 1192 o *newState*: "String" This is the state the client will be in after 1193 applying the set of changes to the old state. 1195 o *hasMoreChanges*: "Boolean" If "true", the client may call _Foo/ 1196 changes_ again with the _newState_ returned to get further 1197 updates. If "false", _newState_ is the current server state. 1199 o *created*: "String[]" An array of ids for records which have been 1200 created since the old state. 1202 o *updated*: "String[]" An array of ids for records which have been 1203 updated since the old state. 1205 o *destroyed*: "String[]" An array of ids for records which have 1206 been destroyed since the old state. 1208 If a record has been created AND updated since the old state, the 1209 server SHOULD just return the id in the _created_ list, but MAY 1210 return it in the _updated_ list as well. 1212 If a record has been updated AND destroyed since the old state, the 1213 server SHOULD just return the id in the _destroyed_ list, but MAY 1214 return it in the _updated_ list as well. 1216 If a record has been created AND destroyed since the old state, the 1217 server SHOULD remove the id from the response entirely, but MAY 1218 include it in the _destroyed_ list, and if so MAY also include it in 1219 the _created_ list. 1221 If a _maxChanges_ is supplied, or set automatically by the server, 1222 the server MUST ensure the number of ids returned across _created_, 1223 _updated_ and _destroyed_ does not exceed this limit. If there are 1224 more changes than this between the client's state and the current 1225 server state, the server SHOULD generate an update to take the client 1226 to an intermediate state, from which the client can continue to call 1227 _Foo/changes_ until it is fully up to date. If it is unable to 1228 calculate an intermediate state, it MUST return a 1229 "cannotCalculateChanges" error response instead. 1231 When generating intermediate states, the server may choose how to 1232 divide up the changes. For many types it will provide a better user 1233 experience to return the more recent changes first, as this is more 1234 likely to be what the user is most interested in. The client can 1235 then continue to page in the older changes while the user is viewing 1236 the newer data. For example, suppose a server went through the 1237 following states: 1239 A -> B -> C -> D -> E 1241 And a client asks for changes from state "B". The server might first 1242 get the ids of records created, updated or destroyed between states D 1243 and E, returning them with: 1245 state: "B-D-E" 1246 hasMoreChanges: true 1248 The client will then ask for the change from state "B-D-E", and the 1249 server can return the changes between states C and D, returning: 1251 state: "B-C-E" 1252 hasMoreChanges: true 1254 Finally the client will request the changes from "B-C-E" and the 1255 server can return the changes between states B and C, returning: 1257 state: "E" 1258 hasMoreChanges: false 1260 Should the state on the server be modified in the middle of all this 1261 (to "F"), the server still does the same but now when the update to 1262 state "E" is returned, it would indicate that it still has more 1263 changes for the client to fetch. 1265 Where multiple changes to a record are split across different 1266 intermediate states, the server MUST NOT return a record as created 1267 in a later response than one which gives it as updated or destroyed, 1268 and MUST NOT return a record as destroyed before a response that 1269 gives it as created or updated. The server may have to coalesce 1270 multiple changes to a record to satisfy this requirement. 1272 The following additional errors may be returned instead of the _Foo/ 1273 changes_ response: 1275 "cannotCalculateChanges": The server cannot calculate the changes 1276 from the state string given by the client. Usually due to the 1277 client's state being too old, or the server being unable to produce 1278 an update to an intermediate state when there are too many updates. 1279 The client MUST invalidate its Foo cache. 1281 Maintaining state to allow calculation of _Foo/changes_ can be 1282 expensive for the server, but always returning 1283 _cannotCalculateChanges_ severely increases network traffic and 1284 resource usage for the client. To allow efficient sync, servers 1285 SHOULD be able to calculate changes from any state string that was 1286 given to a client within the last 30 days (but of course may support 1287 calculating updates from states older than this). 1289 5.3. /set 1291 Modifying the state of Foo objects on the server is done via the 1292 _Foo/set_ method. This encompasses creating, updating and destroying 1293 Foo records. This allows the server to sort out ordering and 1294 dependencies that may exist if doing multiple operations at once (for 1295 example to ensure there is always a minimum number of a certain 1296 record type). 1298 The _Foo/set_ method takes the following arguments: 1300 o *accountId*: "String" The id of the account to use. 1302 o *ifInState*: "String|null" This is a state string as returned by 1303 the _Foo/get_ method. If supplied, the string must match the 1304 current state, otherwise the method will be aborted and a 1305 "stateMismatch" error returned. If "null", any changes will be 1306 applied to the current state. 1308 o *create*: "String[Foo]|null" A map of _creation id_ (an arbitrary 1309 string set by the client) to Foo objects, or "null" if no objects 1310 are to be created. The Foo object type definition MAY define 1311 default values for properties. Any such property MAY be omitted 1312 by the client. The client MUST omit any properties that may only 1313 be set by the server (for example, the _id_ property on most 1314 object types). 1316 o *update*: "String[PatchObject]|null" A map of id to a Patch object 1317 to apply to the current Foo object with that id, or "null" if no 1318 objects are to be updated. A _PatchObject_ is of type 1319 "String[*]", and represents an unordered set of patches. The keys 1320 are a path in [RFC6901] JSON pointer format, with an implicit 1321 leading "/" (i.e. prefix each key with "/" before applying the 1322 JSON pointer evaluation algorithm). All paths MUST also conform 1323 to the following restrictions; if there is any violation, the 1324 update MUST be rejected with an "invalidPatch" error: 1326 * The pointer MUST NOT reference inside an array (i.e. you MUST 1327 NOT insert/delete from an array; the array MUST be replaced in 1328 its entirety instead). 1330 * All parts prior to the last (i.e. the value after the final 1331 slash) MUST already exist on the object being patched. 1333 * There MUST NOT be two patches in the PatchObject where the 1334 pointer of one is the prefix of the pointer of the other, e.g. 1335 "alerts/1/offset" and "alerts". 1337 The value associated with each pointer determines how to apply 1338 that patch: 1340 * If "null", set to the default value if specified for this 1341 property, otherwise remove the property from the patched 1342 object. If the key is not present in the parent, this a no-op. 1344 * Anything else: The value to set for this property (this may be 1345 a replacement or addition to the object being patched). 1347 Any server-set properties MAY be included in the patch if their 1348 value is identical to the current server value (before applying 1349 the patches to the object). Otherwise, the update MUST be 1350 rejected with an _invalidProperties_ SetError. This patch 1351 definition is designed such that an entire Foo object is also a 1352 valid PatchObject. The client MAY choose to optimise network 1353 usage by just sending the diff, or MAY just send the whole object; 1354 the server processes it the same either way. 1356 o *destroy*: "String[]|null" A list of ids for Foo objects to 1357 permanently delete, or "null" if no objects are to be destroyed. 1359 Each creation, modification or destruction of an object is considered 1360 an atomic unit. It is permissible for the server to commit changes 1361 to some objects but not others, however it is not permissible to only 1362 commit part of an update to a single record (e.g. update a _name_ 1363 property but not a _count_ property, if both are supplied in the 1364 update object). 1366 The final state MUST be valid after the Foo/set is finished, however 1367 the server may have to transition through invalid intermediate states 1368 (not exposed to the client) while processing the individual 1369 create/update/destroy requests. For example, suppose there is a 1370 "name" property that must be unique. A single method call could 1371 rename an object A => B, and simultaneously rename another object B 1372 => A. If the final state is valid, this is allowed. Otherwise, each 1373 creation, modification or destruction of an object should be 1374 processed sequentially and accepted/rejected based on the current 1375 server state. 1377 If a create, update or destroy is rejected, the appropriate error 1378 MUST be added to the notCreated/notUpdated/notDestroyed property of 1379 the response and the server MUST continue to the next create/update/ 1380 destroy. It does not terminate the method. 1382 If an id given cannot be found, the update or destroy MUST be 1383 rejected with a "notFound" set error. 1385 The server MAY skip an update (rejecting it with a "willDestroy" 1386 SetError) if that object is destroyed in the same /set request. 1388 Some record objects may hold references to others (foreign keys). 1389 When records are created or modified, they may reference other 1390 records being created _in the same API request_ by using the creation 1391 id prefixed with a "#". The order of the method calls in the request 1392 by the client MUST be such that the record being referenced is 1393 created in the same or an earlier call. The server thus never has to 1394 look ahead. Instead, while processing a request (a series of method 1395 calls), the server MUST keep a simple map for the duration of the 1396 request of creation id to record id for each newly created record, so 1397 it can substitute in the correct value if necessary in later method 1398 calls. 1400 Creation ids are not scoped by type but are a single map for all 1401 types. A client SHOULD NOT reuse a creation id anywhere in the same 1402 API request. If a creation id is reused, the server MUST map the 1403 creation id to the most recently created item with that id. To allow 1404 easy proxying of API requests, an initial set of creation id to real 1405 id values may be passed with a request (see The Request object 1406 specification above). 1408 The response has the following arguments: 1410 o *accountId*: "String" The id of the account used for the call. 1412 o *oldState*: "String|null" The state string that would have been 1413 returned by _Foo/get_ before making the requested changes, or 1414 "null" if the server doesn't know what the previous state string 1415 was. 1417 o *newState*: "String" The state string that will now be returned by 1418 _Foo/get_. 1420 o *created*: "String[Foo]|null" A map of the creation id to an 1421 object containing any properties of the created Foo object that 1422 were not sent by the client. This includes all server-set 1423 properties (such as the _id_ in most object types) and any 1424 properties that were omitted by the client and so set to a default 1425 by the server. This argument is "null" if no Foo objects were 1426 successfully created. 1428 o *updated*: "String[Foo|null]|null" The _keys_ in this map are the 1429 ids of all Foos that were successfully updated, or "null" if none 1430 successful. The _value_ for each id is a Foo object containing 1431 any property that changed in a way _not_ explicitly requested by 1432 the _PatchObject_ sent to the server, or "null" if none. This 1433 lets the client know of any changes to server-set or computed 1434 properties. 1436 o *destroyed*: "String[]|null" A list of Foo ids for records that 1437 were successfully destroyed, or "null" if none successful. 1439 o *notCreated*: "String[SetError]|null" A map of creation id to a 1440 SetError object for each record that failed to be created, or 1441 "null" if all successful. 1443 o *notUpdated*: "String[SetError]|null" A map of Foo id to a 1444 SetError object for each record that failed to be updated, or 1445 "null" if all successful. 1447 o *notDestroyed*: "String[SetError]|null" A map of Foo id to a 1448 SetError object for each record that failed to be destroyed, or 1449 "null" if all successful. 1451 A *SetError* object has the following properties: 1453 o *type*: "String" The type of error. 1455 o *description*: "String|null" A description of the error to help 1456 debug with an explanation of what the problem was. This is a non- 1457 localised string, and is not intended to be shown directly to end 1458 users. 1460 The following SetError types are defined and may be returned for set 1461 operations on any record type where appropriate: 1463 o "forbidden": (create; update; destroy) The create/update/destroy 1464 would violate an ACL or other permissions policy. 1466 o "overQuota": (create) The create would exceed a server-defined 1467 limit on the number or total size of objects of this type. 1469 o "tooLarge": (create; update) The create/update would result in an 1470 object that exceeds a server-defined limit for the maximum size of 1471 a single object of this type. 1473 o "rateLimit": (create) Too many objects of this type have been 1474 created recently, and a server-defined rate limit has been 1475 reached. It may work if tried again later. 1477 o "notFound": (update; destroy) The id given to update/destroy 1478 cannot be found. 1480 o "invalidPatch": (update) The PatchObject given to update the 1481 record was not a valid patch (see the patch description). 1483 o "willDestroy" (update) The client requested an object be both 1484 updated and destroyed in the same /set request, and the server has 1485 decided to therefore ignore the update. 1487 o "invalidProperties": (create; update) The record given is invalid 1488 in some way. For example: 1490 * It contains properties which are invalid according to the type 1491 specification of this record type. 1493 * It contains a property that may only be set by the server (e.g. 1494 "id") and are different to the current value. Note, to allow 1495 clients to pass whole objects back, it is not an error to 1496 include a server-set property so long as the value is identical 1497 to the current value on the server (or the value that will be 1498 set by the server if a create). 1500 * There is a reference to another record (foreign key) and the 1501 given id does not correspond to a valid record. 1503 The SetError object SHOULD also have a property called 1504 _properties_ of type "String[]" that lists *all* the properties 1505 that were invalid. Individual methods MAY specify more specific 1506 errors for certain conditions that would otherwise result in an 1507 invalidProperties error. If the condition of one of these is met, 1508 it MUST be returned instead of the invalidProperties error. 1510 o "singleton": (create; destroy) This is a singleton type, so you 1511 cannot create another one or destroy the existing one. 1513 Other possible SetError types MAY be given in specific method 1514 descriptions. Other properties MAY also be present on the _SetError_ 1515 object, as described in the relevant methods. 1517 The following additional errors may be returned instead of the _Foo/ 1518 set_ response: 1520 "requestTooLarge": The total number of objects to create, update or 1521 destroy exceeds the maximum number the server is willing to process 1522 in a single method call. 1524 "stateMismatch": An "ifInState" argument was supplied and it does not 1525 match the current state. 1527 5.4. /copy 1529 The only way to move Foo records *between* two different accounts is 1530 to copy them using the _Foo/copy_ method, then once the copy has 1531 succeeded, delete the original. The _onSuccessDestroyOriginal_ 1532 argument allows you to try to do this in one method call, however 1533 note that the two different actions are not atomic, and so it is 1534 possible for the copy to succeed but the original not to be destroyed 1535 for some reason. 1537 The _Foo/copy_ method takes the following arguments: 1539 o *fromAccountId*: "String" The id of the account to copy records 1540 from. 1542 o *toAccountId*: "String" The id of the account to copy records to. 1543 This MUST be different to the "fromAccountId". 1545 o *create*: "String[Foo]" A map of _creation id_ to a Foo object. 1546 The object MUST contain an id property: the id of the record to be 1547 copied. Any other properties included are used instead of the 1548 current value for that property on the original when creating the 1549 copy. 1551 o *onSuccessDestroyOriginal*: "Boolean" (default: false) If "true", 1552 an attempt will be made to destroy the original records that were 1553 successfully copied: after emitting the _Foo/copy_ response, but 1554 before processing the next method, the server MUST make a single 1555 call to _Foo/set_ to destroy the original of each successfully 1556 copied record; the output of this is added to the responses as 1557 normal to be returned to the client. 1559 Each record copy is considered an atomic unit which may succeed or 1560 fail individually. 1562 The response has the following arguments: 1564 o *fromAccountId*: "String" The id of the account records were 1565 copied from. 1567 o *toAccountId*: "String" The id of the account records were copied 1568 to. 1570 o *created*: "String[Foo]|null" A map of the creation id to an 1571 object containing any properties of the copied Foo object that are 1572 set by the server (such as the _id_ in most object types). This 1573 argument is "null" if no Foo objects were successfully copied. 1575 o *notCreated*: "String[SetError]|null" A map of creation id to a 1576 SetError object for each record that failed to be copied, "null" 1577 if none. 1579 The *SetError* may be any of the standard set errors that may be 1580 returned for a _create_ or _update_. In addition, the following 1581 SetError is defined: 1583 "alreadyExists": The server forbids duplicates and the record already 1584 exists in the target account. An _existingId_ property of type 1585 "String" MUST be included on the error object with the id of the 1586 existing record. 1588 The following additional errors may be returned instead of the _Foo/ 1589 copy_ response: 1591 "fromAccountNotFound": The _fromAccountId_ does not correspond to a 1592 valid account. 1594 "toAccountNotFound": The _toAccountId_ does not correspond to a valid 1595 account. 1597 "fromAccountNotSupportedByMethod": The _fromAccountId_ given 1598 corresponds to a valid account, but the account does not support this 1599 data type. 1601 "toAccountNotSupportedByMethod": The _toAccountId_ given corresponds 1602 to a valid account, but the account does not support this data type. 1604 5.5. /query 1606 For data sets where the total amount of data is expected to be very 1607 small, clients can just fetch the complete set of data and then do 1608 any sorting/filtering locally. However, for large data sets (e.g. 1609 multi-gigabyte mailboxes), the client needs to be able to 1610 search/sort/window the data type on the server. 1612 A query on the set of Foos in an account is made by calling _Foo/ 1613 query_. This takes a number of arguments to determine which records 1614 to include, how they should be sorted, and which part of the result 1615 should be returned (the full list may be _very_ long). The result is 1616 returned as a list of Foo ids. 1618 A call to _Foo/query_ takes the following arguments: 1620 o *accountId*: "String" The id of the account to use. 1622 o *filter*: "FilterOperator|FilterCondition|null" Determines the set 1623 of Foos returned in the results. If "null", all objects in the 1624 account of this type are included in the results. A 1625 *FilterOperator* object has the following properties: 1627 * *operator*: "String" This MUST be one of the following strings: 1628 "AND"/"OR"/"NOT": 1630 + *AND*: all of the conditions must match for the filter to 1631 match. 1633 + *OR*: at least one of the conditions must match for the 1634 filter to match. 1636 + *NOT*: none of the conditions must match for the filter to 1637 match. 1639 * *conditions*: "(FilterOperator|FilterCondition)[]" The 1640 conditions to evaluate against each record. 1642 A *FilterCondition* is an "object", whose allowed properties and 1643 semantics depend on the data type and is defined in the _/query_ 1644 method specification for that type. It MUST NOT have an 1645 _operator_ property. 1647 o *sort*: "Comparator[]|null" Lists the names of properties to 1648 compare between two Foo records, and how to compare them, to 1649 determine which comes first in the sort. If two Foo records have 1650 an identical value for the first comparator, the next comparator 1651 will be considered and so on. If all comparators are the same 1652 (this includes the case where an empty array or "null" is given as 1653 the _sort_ argument), the sort order is server-dependent, but MUST 1654 be stable between calls to Foo/query. A *Comparator* has the 1655 following properties: 1657 * *property*: "String" The name of the property on the Foo 1658 objects to compare. 1660 * *isAscending*: "Boolean" (optional; default: true) If true, 1661 sort in ascending order. If false, reverse the comparator's 1662 results to sort in descending order. 1664 * *collation*: "String" (optional; default is server-dependent) 1665 The identifier, as registered in the collation registry defined 1666 in [RFC4790], for the algorithm to use when comparing the order 1667 of strings. The algorithms the server supports are advertised 1668 in the capabilities object returned with the JMAP Session 1669 object. If omitted, the default algorithm is server-dependent, 1670 but: 1672 1. It MUST be unicode-aware. 1674 2. It SHOULD have reasonable default behavior for many 1675 languages when the user's language is unknown. 1677 3. It MAY be selected based on out-of-band information about 1678 the user's language/locale. 1680 4. It SHOULD be case-insensitive where such a concept makes 1681 sense for a language/locale. 1683 The "i;unicode-casemap" collation ([RFC5051]) and the Unicode 1684 Collation Algorithm () 1685 are two examples that fulfil these criterion. When the 1686 property being compared is not a string, the _collation_ 1687 property is ignored and the following comparison rules apply 1688 based on the type. In ascending order: 1690 + "Boolean": "false" comes before "true". 1692 + "Number": A lower number comes before a higher number. 1694 + "Date"/"UTCDate": The earlier date comes first. 1696 The object may also have additional properties as required for 1697 specific sort operations defined in a type's /query method. 1699 o *position*: "Int" (default: 0) The 0-based index of the first id 1700 in the full list of results to return. If a negative value is 1701 given, it is an offset from the end of the list. Specifically, 1702 the negative value MUST be added to the total number of results 1703 given the filter, and if still negative clamped to "0". This is 1704 now the 0-based index of the first id to return. If the index is 1705 greater than or equal to the total number of objects in the 1706 results list then the _ids_ array in the response will be empty, 1707 but this is not an error. 1709 o *anchor*: "String|null" A Foo id. If supplied the _position_ 1710 argument is ignored. The index of this id in the results will be 1711 used in combination with the "anchorOffset" argument to determine 1712 the index of the first result to return (see below for more 1713 details). 1715 o *anchorOffset*: "Int|null" The index of the first result to return 1716 relative to the index of the anchor. This MAY be negative. For 1717 example, "-1" means the first Foo before the anchor Foo should be 1718 the first result in the results returned (see below for more 1719 details). 1721 o *limit*: "PositiveInt|null" The maximum number of results to 1722 return. If "null", no limit presumed. The server MAY choose to 1723 enforce a maximum "limit" argument. In this case, if a greater 1724 value is given (or if it is "null"), the limit should be clamped 1725 to the maximum; since the total number of results in the query is 1726 returned, the client can determine if it has received all the 1727 results. If a negative value is given, the call MUST be rejected 1728 with an "invalidArguments" error. 1730 o *calculateTotal*: "Boolean" (default: false) Does the client wish 1731 to know the total number of results in the query? This may be 1732 slow and expensive for servers to calculate, particularly with 1733 complex filters, so clients should take care to only request the 1734 total when needed. 1736 If an *anchor* argument is given, then after filtering and sorting 1737 the anchor is looked for in the results. If found, the *anchor 1738 offset* is then added to its index. If the resulting index is now 1739 negative, it is clamped to 0. This index is now used exactly as 1740 though it were supplied as the "position" argument. If the anchor is 1741 not found, the call is rejected with an "anchorNotFound" error. 1743 If an _anchor_ is specified, any position argument supplied by the 1744 client MUST be ignored. If _anchorOffset_ is "null", it defaults to 1745 "0". If no _anchor_ is supplied, any anchor offset argument MUST be 1746 ignored. 1748 A client can use _anchor_ instead of _position_ to find the index of 1749 an id within a large set of results. 1751 The response has the following arguments: 1753 o *accountId*: "String" The id of the account used for the call. 1755 o *filter*: "FilterOperator|FilterCondition|null" The filter used. 1756 Echoed back from the call. 1758 o *sort*: "Comparator[]|null" The sort options used. Echoed back 1759 from the call. 1761 o *queryState*: "String" A string encoding the current state of the 1762 query on the server. This string MUST change if the results of 1763 the query (i.e. the matching ids and their sort order) have 1764 changed. The queryState string MAY change if something has 1765 changed on the server which means the results may have changed but 1766 the server doesn't know for sure. The queryState string only 1767 represents the ordered list of ids that match the particular query 1768 (including its sort/filter). There is no requirement for it to 1769 change if a property on an object matching the query changes but 1770 the query results are unaffected (indeed, it is more efficient if 1771 the queryState string does not change in this case). The 1772 queryState string only has meaning when compared to future 1773 responses to a query with the same type/sort/filter, or when used 1774 with /queryChanges to fetch changes. Should a client receive back 1775 a response with a different queryState string to a previous call, 1776 it MUST either throw away the currently cached query and fetch it 1777 again (note, this does not require fetching the records again, 1778 just the list of ids) or, call _Foo/queryChanges_ to get the 1779 difference. 1781 o *canCalculateChanges*: "Boolean" This is "true" if the server 1782 supports calling _Foo/queryChanges_ with these "filter"/"sort" 1783 parameters. Note, this does not guarantee that the _Foo/ 1784 queryChanges_ call will succeed, as it may only be possible for a 1785 limited time afterwards due to server internal implementation 1786 details. 1788 o *position*: "PositiveInt" The 0-based index of the first result in 1789 the "ids" array within the complete list of query results. 1791 o *total*: "PositiveInt" (only if requested) The total number of 1792 foos in the results (given the _filter_). This argument MUST be 1793 omitted if the _calculateTotal_ request argument is not "true". 1795 o *ids*: "String[]" The list of ids for each foo in the query 1796 results, starting at the index given by the _position_ argument of 1797 this response, and continuing until it hits the end of the results 1798 or reaches the "limit" number of ids. If _position_ is >= 1799 _total_, this MUST be the empty list. 1801 The following additional errors may be returned instead of the _Foo/ 1802 query_ response: 1804 "anchorNotFound": An anchor argument was supplied, but it cannot be 1805 found in the results of the query. 1807 "unsupportedSort": The _sort_ is syntactically valid, but includes a 1808 property the server does not support sorting on, or a collation 1809 method it does not recognise. 1811 "unsupportedFilter": The _filter_ is syntactically valid, but the 1812 server cannot process it. If the filter was the result of a user's 1813 search input, the client SHOULD suggest the user simplify their 1814 search. 1816 5.6. /queryChanges 1818 The "Foo/queryChanges" call allows a client to efficiently update the 1819 state of any cached foo query to match the new state on the server. 1820 It takes the following arguments: 1822 o *accountId*: "String" The id of the account to use. 1824 o *filter*: "FilterOperator|FilterCondition|null" The filter 1825 argument that was used with _Foo/query_. 1827 o *sort*: "Comparator[]|null" The sort argument that was used with 1828 _Foo/query_. 1830 o *sinceQueryState*: "String" The current state of the query in the 1831 client. This is the string that was returned as the _queryState_ 1832 argument in the _Foo/query_ response with the same sort/filter. 1833 The server will return the changes made to the query since this 1834 state. 1836 o *maxChanges*: "PositiveInt|null" The maximum number of changes to 1837 return in the response. See error descriptions below for more 1838 details. 1840 o *upToId*: "String|null" The last (highest-index) id the client 1841 currently has cached from the query results. When there are a 1842 large number of results, in a common case the client may have only 1843 downloaded and cached a small subset from the beginning of the 1844 results. If the sort and filter are both only on immutable 1845 properties, this allows the server to omit changes after this 1846 point in the results, which can significantly increase efficiency. 1847 If they are not immutable, this argument is ignored. 1849 o *calculateTotal*: "Boolean" (default: false) Does the client wish 1850 to know the total number of results now in the query? This may be 1851 slow and expensive for servers to calculate, particularly with 1852 complex filters, so clients should take care to only request the 1853 total when needed. 1855 The response has the following arguments: 1857 o *accountId*: "String" The id of the account used for the call. 1859 o *filter*: "FilterOperator|FilterCondition|null" The filter used. 1860 Echoed back from the call. 1862 o *sort*: "Comparator[]|null" The sort options used. Echoed back 1863 from the call. 1865 o *oldQueryState*: "String" This is the "sinceQueryState" argument 1866 echoed back; the state from which the server is returning changes. 1868 o *newQueryState*: "String" This is the state the query will be in 1869 after applying the set of changes to the old state. 1871 o *upToId*: "String|null" Echoed back from the call. 1873 o *total*: "PositiveInt" (only if requested) The total number of 1874 foos in the results (given the _filter_). This argument MUST be 1875 omitted if the _calculateTotal_ request argument is not "true". 1877 o *removed*: "String[]" The _id_ for every foo that was in the query 1878 results in the old state and is not in the results in the new 1879 state. If the sort and filter are both only on immutable 1880 properties and an _upToId_ is supplied and exists in the results, 1881 any ids that were removed but have a higher index than _upToId_ 1882 SHOULD be omitted. If the server cannot calculate this exactly, 1883 the server MAY return extra foos in addition that may have been in 1884 the old results but are not in the new results. If the _filter_ 1885 or _sort_ includes a mutable property, the server MUST include all 1886 foos in the current results for which this property MAY have 1887 changed. 1889 o *added*: "AddedItem[]" The id and index in the query results (in 1890 the new state) for every foo that has been added to the results 1891 since the old state AND every foo in the current results that was 1892 included in the _removed_ array (due to a filter or sort based 1893 upon a mutable property). If the sort and filter are both only on 1894 immutable properties and an _upToId_ is supplied and exists in the 1895 results, any ids that were added but have a higher index than 1896 _upToId_ SHOULD be omitted. The array MUST be sorted in order of 1897 index, lowest index first. An *AddedItem* object has the 1898 following properties: 1900 * *id*: "String" 1902 * *index*: "PositiveInt" 1904 The result of this is that if the client has a cached sparse array of 1905 foo ids in the results in the old state: 1907 fooIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ] 1909 then if it *splices out* all foos in the removed array: 1911 removed = [ "id2", ... ]; 1912 fooIds => [ "id1", null, null, "id3", "id4", null, null, null ] 1914 and *splices in* (in order) all of the foos in the added array: 1916 added = [{ id: "id5", index: 0, ... }]; 1917 fooIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ] 1919 and *truncates* or *extends* to the new total length, then the 1920 results will now be in the new state. 1922 The following additional errors may be returned instead of the _Foo/ 1923 queryChanges_ response: 1925 "tooManyChanges": There are more changes than the client's 1926 _maxChanges_ argument. Each item in the removed or added array is 1927 considered as one change. The client may retry with a higher max 1928 changes or invalidate its cache of the query results. 1930 "cannotCalculateChanges": The server cannot calculate the changes 1931 from the queryState string given by the client. Usually due to the 1932 client's state being too old. The client MUST invalidate its cache 1933 of the query results. 1935 5.7. Examples 1937 Suppose we have a type _Todo_ with the following properties: 1939 o *id*: "String" (immutable; server-set) The id of the object. 1941 o *title*: "String" A brief summary of what is to be done. 1943 o *keywords*: "String[Boolean]" (default: ) A set of keywords that 1944 apply to the todo. The set is represented as an object, with the 1945 keys being the _keywords_. The value for each key in the object 1946 MUST be "true". 1948 o *neuralNetworkTimeEstimation*: "Number" (server-set) The title and 1949 keywords are fed into the server's state-of-the-art neural network 1950 to get an estimation of how long this todo will take, in seconds. 1952 and the server supports all the standard methods for the type, 1953 including querying by keyword using the syntax "{ hasKeyword: "foo" 1954 }" in the _filter_ argument to _/query_. 1956 Now, a client might want to display the list of todos with either a 1957 "music" keyword or a "video" keyword, so it makes the following 1958 method call: 1960 [[ "Todo/query", { 1961 "accountId": "x", 1962 "filter": { 1963 "operator": "OR", 1964 "conditions": [ 1965 { "hasKeyword": "music" }, 1966 { "hasKeyword": "video" } 1967 ] 1968 }, 1969 "sort": [{ "property": "title" }], 1970 "position": 0, 1971 "limit": 10 1972 }, "0" ], 1973 [ "Todo/get", { 1974 "accountId": "x", 1975 "#ids": { 1976 "resultOf": "0", 1977 "name": "Todo/query", 1978 "path": "/ids" 1979 }, 1980 }, "1" ]] 1982 This would query the server for the set of todos with a keyword of 1983 "music", sorted by title, and limited to the first 10 results. It 1984 fetches the full object for each of these Todos using backreferences 1985 to reference the result of the query. The response might look 1986 something like: 1988 [[ "Todo/query", { 1989 "accountId": "x", 1990 "filter": { 1991 "operator": "OR", 1992 "conditions": [ 1993 { "hasKeyword": "music" }, 1994 { "hasKeyword": "video" } 1995 ] 1996 }, 1997 "sort": [{ "property": "title" }], 1998 "queryState": "y13213", 1999 "canCalculateChanges": true, 2000 "position": 0, 2001 "ids": [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ] 2002 }, "0" ], 2003 [ "Todo/get", { 2004 "accountId": "x", 2005 "state": "10324", 2006 "list": [{ 2007 "id": "a", 2008 "title": "Practise Piano", 2009 "keywords": { 2010 "music": true, 2011 "beethoven": true, 2012 "mozart": true, 2013 "liszt": true, 2014 "rachmaninov": true 2015 }, 2016 "neuralNetworkTimeEstimation": 3600 2017 }, { 2018 "id": "b", 2019 "title": "Listen to Daft Punk", 2020 "keywords": { 2021 "music": true, 2022 "trance": true 2023 }, 2024 "neuralNetworkTimeEstimation": 18000 2025 }, 2026 ... 2027 ] 2028 }, "1" ]] 2030 Now suppose the user adds a keyword "chopin" and removes the keyword 2031 "mozart" from the "Practise Piano" task. The client may send the 2032 whole object to the server, as this is a valid PatchObject: 2034 [[ "Todo/set", { 2035 "accountId": "x", 2036 "ifInState": "10324", 2037 "update": { 2038 "a": { 2039 "id": "a", 2040 "title": "Practise Piano", 2041 "keywords": { 2042 "music": true, 2043 "beethoven": true, 2044 "chopin": true, 2045 "liszt": true, 2046 "rachmaninov": true, 2047 } 2048 "neuralNetworkTimeEstimation": 360 2049 } 2050 } 2051 }, "0" ]] 2053 or it may send a minimal patch: 2055 [[ "Todo/set", { 2056 "accountId": "x", 2057 "ifInState": "10324", 2058 "update": { 2059 "a": { 2060 "keywords/chopin": true, 2061 "keywords/mozart": null 2062 } 2063 } 2064 }, "0" ]] 2066 The effect is exactly the same on the server in either case, and 2067 presuming the server is still in state "10324" it will probably 2068 return success: 2070 [[ "Todo/set", { 2071 "accountId": "x", 2072 "oldState": "10324", 2073 "newState": "10329", 2074 "updated": { 2075 "a": { 2076 "neuralNetworkTimeEstimation": 5400 2077 } 2078 } 2079 }, "0" ]] 2081 The server changed the "neuralNetworkTimeEstimation" property on the 2082 object as part of this change; as this changed in a way _not_ 2083 explicitly requested by the PatchObject sent to the server, it is 2084 returned with the "updated" confirmation. 2086 Now, suppose another user deleted the "Listen to Daft Punk" todo. 2087 The first user will receive a push notification (see later in the 2088 spec) with the changed state string for the "Todo" type. Since the 2089 new string does not match its current state, it knows it needs to 2090 check for updates. It may make a request like: 2092 [[ "Todo/changes", { 2093 "accountId": "x", 2094 "sinceState": "10324", 2095 "maxChanges": 50, 2096 }, "0" ], 2097 [ "Todo/queryChanges", { 2098 "accountId": "x", 2099 "filter": { 2100 "operator": "OR", 2101 "conditions": [ 2102 { "hasKeyword": "music" }, 2103 { "hasKeyword": "video" } 2104 ] 2105 }, 2106 "sort": [{ "property": "title" }], 2107 "sinceQueryState": "y13213" 2108 "maxChanges": 50, 2109 }, "1" ]] 2111 and receive in response: 2113 [[ "Todo/changes", { 2114 "accountId": "x", 2115 "oldState": "10324", 2116 "newState": "871903", 2117 "hasMoreChanges": false, 2118 "created": [], 2119 "updated": [], 2120 "destroyed": ["b"] 2121 }, "0" ], 2122 [ "Todo/queryChanges", { 2123 "accountId": "x", 2124 "filter": { 2125 "operator": "OR", 2126 "conditions": [ 2127 { "hasKeyword": "music" }, 2128 { "hasKeyword": "video" } 2129 ] 2130 }, 2131 "sort": [{ "property": "title" }], 2132 "oldQueryState": "y13213" 2133 "newQueryState": "y13218" 2134 "removed": ["b"], 2135 "added": null 2136 }, "1" ]] 2138 Suppose the user has access to another account "y", for example a 2139 team account shared between multiple users. To move an existing Todo 2140 from account "x", the client would call: 2142 [[ "Todo/copy", { 2143 "fromAccountId": "x", 2144 "toAccountId": "y", 2145 "create": { 2146 "k5122": { 2147 "id": "a" 2148 } 2149 }, 2150 "onSuccessDestroyOriginal": true, 2151 }, "0" ]] 2153 The server successfully copies the Todo to a new account (where it 2154 receives a new id) and deletes the original. Due to the implicit 2155 call to "Todo/set", there are two responses to the single method 2156 call, both with the same client id: 2158 [[ "Todo/copy", { 2159 "fromAccountId": "x", 2160 "toAccountId": "y", 2161 "created": { 2162 "k5122": { 2163 "id": "97" 2164 } 2165 }, 2166 "notCreated": null 2167 }, "0" ], 2168 [ "Todo/set", { 2169 "accountId": "x", 2170 "oldState": "871903" 2171 "newState": "871909", 2172 "destroyed": [ "a" ], 2173 ... 2174 }, "0" ]] 2176 5.8. Proxy considerations 2178 JMAP has been designed to allow an API endpoint to easily proxy 2179 through to one or more JMAP servers. This may be useful for load 2180 balancing, augmenting capabilities, or presenting a single endpoint 2181 to accounts hosted on different JMAP servers (splitting the request 2182 based on each method's "accountId" argument). The proxy need only 2183 understand the general structure of a JMAP Request object, it does 2184 not need to know anything specifically about the methods and 2185 arguments it will pass through to other servers. 2187 If splitting up the methods in a request to call them on different 2188 backend servers, the proxy must do two things to ensure back- 2189 references and creation id references resolve the same as if the 2190 entire request were processed on a single server: 2192 1. It must pass a "createdIds" property with each subrequest. If 2193 this is not given by the client, an empty object should be used 2194 for the first subrequest. The "createIds" property of each 2195 subresponse should be passed on in the next subrequest. 2197 2. It must resolve back references to previous method results that 2198 were processed on a different server. This is a relatively 2199 simple syntactic substitution, described in section 3.6. 2201 When splitting a request based on accountId, proxy implementors do 2202 need to be aware of "/copy" methods, that copy between accounts. If 2203 the accounts are on different servers, the proxy will have to 2204 implement this functionality directly. 2206 6. Binary data 2208 Binary data is referenced by a _blobId_ in JMAP, and uploaded/ 2209 downloaded separately to the core API. The blobId solely represents 2210 the raw bytes of data, not any associated metadata such as a file 2211 name or content type. Such metadata is stored alongside the blobId 2212 in the object referencing it. The data represented by a blobId is 2213 immutable. 2215 Any blobId that exists within an account may be used when creating/ 2216 updating another object in that account. For example, an Email type 2217 may have a blobId that represents the [RFC5322] representation of the 2218 message. A client could create a new Email object with an attachment 2219 and use this blobId, in effect attaching the old message to the new 2220 one. Similarly it could attach any existing attachment of an old 2221 message without having to download and upload it again. 2223 When the client uses a blobId in a create/update, the server MAY 2224 assign a new blobId to refer to the same binary data within the new/ 2225 updated object. If it does so, it MUST return any properties that 2226 contain a changed blobId in the created/updated response so the 2227 client gets the new ids. 2229 A blob that is not referenced by a JMAP object (e.g. as a message 2230 attachment) MAY be deleted by the server to free up resources. 2231 Uploads (see below) are initially unreferenced blobs. To ensure 2232 interoperability: 2234 o The server SHOULD use a separate quota for unreferenced blobs to 2235 the user's usual quota. 2237 o This quota SHOULD be at least the maximum total size that a single 2238 object can reference on this server. For example, if supporting 2239 JMAP Mail, this should be at least the maximum total attachments 2240 size for a message. 2242 o When an upload would take the user over quota, the server MUST 2243 delete unreferenced blobs in date order, oldest first, until there 2244 is room for the new blob. 2246 o Except where quota restrictions force early deletion, an 2247 unreferenced blob MUST NOT be deleted for at least 1 hour from the 2248 time of upload; if reuploaded, the same blobId MAY be returned, 2249 but this SHOULD reset the expiry time. 2251 o A blob MUST NOT be deleted during the method call which removed 2252 the last reference, so that a client can issue a create and a 2253 destroy that both reference the blob within the same method call. 2255 6.1. Uploading binary data 2257 There is a single endpoint which handles all file uploads for an 2258 account, regardless of what they are to be used for. The JMAP 2259 Session object has an _uploadUrl_ property in [RFC6570] URI Template 2260 (level 1) format, which MUST contain a variable called "accountId". 2261 The client may use this template in combination with an _accountId_ 2262 to get the URL of the file upload resource. 2264 To upload a file, the client submits an authenticated POST request to 2265 the file upload resource. 2267 A successful request MUST return a single JSON object with the 2268 following properties as the response: 2270 o *accountId*: "String" The id of the account used for the call. 2272 o *blobId*: "String", The id representing the binary data uploaded. 2273 The data for this id is immutable. The id _only_ refers to the 2274 binary data, not any metadata. 2276 o *type*: "String" The media type of the file (as specified in 2277 [RFC6838], section 4.2) as set in the Content-Type header of the 2278 upload HTTP request. 2280 o *size*: "PositiveInt" The size of the file in octets. 2282 If identical binary content to an existing blob in the account is 2283 uploaded, the existing blobId MAY be returned. 2285 When an HTTP error response is returned to the client, the server 2286 SHOULD return a JSON "problem details" object as the response body, 2287 as per [RFC7807]. 2289 6.2. Downloading binary data 2291 The JMAP Session object has a _downloadUrl_ property, which is in 2292 [RFC6570] URI Template (level 1) format. The URL MUST contain 2293 variables called "accountId", "blobId", "type" and "name". 2295 To download a file, the client makes an authenticated GET request to 2296 the download URL with the appropriate variables substituted in: 2298 o "accountId": The id of the account to which the record with the 2299 blobId belongs. 2301 o "blobId": The blobId representing the data of the file to 2302 download. 2304 o "type": The type for the server to set in the "Content-Type" 2305 header of the response; the blobId only represents the binary data 2306 and does not have a content-type inately associated with it. 2308 o "name": The name for the file; the server MUST return this as the 2309 filename if it sets a "Content-Disposition" header. 2311 As the data for a particular blobId is immutable, and thus the 2312 response in the generated download URL is too, implementors are 2313 recommended to set long cache times for successful responses, for 2314 example "Cache-Control: private, max-age=31536000". 2316 When an HTTP error response is returned to the client, the server 2317 SHOULD return a JSON "problem details" object as the response body, 2318 as per [RFC7807]. 2320 6.3. Blob/copy 2322 Binary data may be copied *between* two different accounts using the 2323 _Blob/copy_ method, rather than having to download then reupload on 2324 the client. 2326 The _Blob/copy_ method takes the following arguments: 2328 o *fromAccountId*: "String" The id of the account to copy blobs 2329 from. 2331 o *toAccountId*: "String" The id of the account to copy blobs to. 2333 o *blobIds*: "String[]" A list of ids of blobs to copy to the other 2334 account. 2336 The response has the following arguments: 2338 o *fromAccountId*: "String" The id of the account emails were copied 2339 from. 2341 o *toAccountId*: "String" The id of the account emails were copied 2342 to. 2344 o *copied*: "String[String]|null" A map of the blobId in the 2345 _fromAccount_ to the id for the blob in the _toAccount_, or "null" 2346 if none were successfully copied. 2348 o *notCopied*: "String[SetError]|null" A map of blobId to a SetError 2349 object for each blob that failed to be copied, "null" if none. 2351 The *SetError* may be any of the standard set errors that may be 2352 returned for a _create_. In addition, the "notFound" SetError error 2353 may be returned if the blobId to be copied cannot be found. 2355 The following additional errors may be returned instead of the _Blob/ 2356 copy_ response: 2358 "fromAccountNotFound": A _fromAccountId_ was explicitly included with 2359 the request, but it does not correspond to a valid account. 2361 "toAccountNotFound": A _toAccountId_ was explicitly included with the 2362 request, but it does not correspond to a valid account. 2364 7. Push 2366 Push notifications allow clients to efficiently update (almost) 2367 instantly to stay in sync with data changes on the server. In JMAP, 2368 push notifications occur out-of-band (i.e. not over the same 2369 connection as API exchanges), so that they can make use of efficient 2370 native push mechanisms on different platforms. 2372 The general model for push is simple and sends minimal data over the 2373 push channel. The format allows multiple changes to be coalesced 2374 into a single push update, and the frequency of pushes to be rate 2375 limited by the server. It doesn't matter if some push events are 2376 dropped before they reach the client; it will still get all changes 2377 next time it syncs. 2379 7.1. The StateChange object 2381 When something changes on the server, the server pushes a 2382 *StateChange* object to the client. A *StateChange* object has the 2383 following properties: 2385 o *changed*: "String[TypeState]" A map of _account id_ to an object 2386 encoding the state of data types that have changed for that 2387 account since the last push event, for each of the accounts to 2388 which the user has access and for which something has changed. A 2389 *TypeState* object is a map. The keys are the type name "Foo" 2390 (e.g. "Mailbox" or "Email"), and the value is the _state_ 2391 property that would currently be returned by a call to _Foo/get_. 2392 The client can compare the new state strings with its current 2393 values to see whether it has the current data for these types. If 2394 not, the changes can then be efficiently fetched in a single 2395 standard API request (using the _/changes_ type methods). 2397 7.1.1. Example 2399 In this example, the server has almalgamated a few changes together 2400 across two different accounts the user has access to, before pushing 2401 the following StateChange object to the client: 2403 { 2404 "changed": { 2405 "a3123": { 2406 "Email": "d35ecb040aab", 2407 "EmailDelivery": "428d565f2440", 2408 "CalendarEvent": "87accfac587a" 2409 }, 2410 "a43461d": { 2411 "Mailbox": "0af7a512ce70", 2412 "CalendarEvent": "7a4297cecd76" 2413 }, 2414 } 2415 } 2417 The client can compare the state strings with its current state for 2418 the Email, CalendarEvent etc. object types in the appropriate 2419 accounts to see if it needs to fetch changes. If the client is 2420 itself making changes, it may receive a StateChange object while the 2421 /set API call is in flight. It can wait until the call completes and 2422 then compare if the new state string after the /set is the same as 2423 was pushed in the StateChange object; if so, it does not need to 2424 waste a request asking for changes it already knows. 2426 7.2. PushSubscription 2428 A push subscription is a message delivery context established between 2429 the client and a push service. A *PushSubscription* object has the 2430 following properties: 2432 o *id*: "String" (immutable; server-set) The id of the push 2433 subscription. 2435 o *deviceClientId*: "String" (immutable) An id that uniquely 2436 identifies the client + device it is running on. The purpose of 2437 this is to allow clients to identify which PushSubscription 2438 objects they created even if they lose their local state, so they 2439 can revoke or update them. This string MUST be different on 2440 different devices, and be different from other vendors. It SHOULD 2441 be easy to re-generate, not depend on persisted state. A secure 2442 hash that includes both a device id and vendor id is one way this 2443 could be achieved. 2445 o *url*: "String" (immutable) An absolute URL where the JMAP server 2446 will POST the data for the push message. This MUST begin with 2447 "https://". 2449 o *keys*: "Object|null" (immutable) Client-generated encryption 2450 keys. If supplied the server MUST use them as specified in 2451 [RFC8291] to encrypt all data sent to the push subscription. The 2452 object MUST have the following properties: 2454 * *p256dh*: the P-256 ECDH Diffie-Hellman public key as described 2455 in [RFC8291], encoded in URL-safe Base64 representation as 2456 defined in [RFC4648]. 2458 * *auth*: the authentication secret as described in [RFC8291], 2459 encoded in URL-safe base64 representation as defined in 2460 [RFC4648]. 2462 o *expires*: "UTCDate|null" The time this push subscription expires. 2463 If specified, the JMAP server MUST NOT make further requests to 2464 this resource after this time. It MAY automatically destroy the 2465 push subscription at or after this time. The server MAY choose to 2466 set an expiry if none is given by the client, or modify the expiry 2467 time given by the client to a shorter duration. 2469 o *types*: "String[]|null" A list of types the client is interested 2470 in (using the same names as the keys in the _TypeState_ object). 2471 Push events will only be sent if the data for one of these types 2472 changes. Other types are omitted from the TypeState object. If 2473 "null", changes will be pushed for all types. 2475 Clients may create a push subscription on the JMAP server, which will 2476 then make a POST request to the associated push endpoint whenever an 2477 event occurs. 2479 The POST request MUST have a content type of "application/json" and 2480 contain the UTF-8 JSON encoded _StateChange_ object as the body. The 2481 request MUST have a "TTL" header, and MAY have "Urgency" and/or 2482 "Topic" headers, as specified in section 5 of [RFC8030]. 2484 If the response code is "503" (Service Unavailable), the JMAP server 2485 MAY try again later, but may also just drop the event. If the 2486 response code is "429" (Too Many Requests) the JMAP server SHOULD 2487 attempt to reduce the frequency of pushes to that URL. Any other 2488 "4xx" or "5xx" response code MUST be considered a *permanent failure* 2489 and the push subscription SHOULD be destroyed. 2491 The use of this push endpoint conforms with the use of a push 2492 endpoint by an Application Server as defined in [RFC8030]. A client 2493 MAY use the rest of [RFC8030] in combination with its own Push Server 2494 to form a complete end-to-end solution, or MAY rely on alternative 2495 mechanisms to ensure the delivery of the pushed data after it leaves 2496 the JMAP server. 2498 The push subscription is tied to the credentials used to authenticate 2499 the API request that created it. Should these credentials expire or 2500 be revoked, the push subscription MUST be destroyed by the JMAP 2501 server. 2503 When these credentials have their own expiry (i.e. it is a session 2504 with a timeout), the server SHOULD NOT set or bound the expiry time 2505 for the push subscription given by the client, but MUST expire it 2506 when the session expires. 2508 When these credentials are not time bounded (e.g. [RFC7617] Basic 2509 Authentication), the server SHOULD set an expiry time for the push 2510 subscription if none given, and limit the expiry time if set too far 2511 in the future. This maximum expiry time MUST be at least 48 hours in 2512 the future and SHOULD be at least 7 days in the future. 2514 In the case of separate access and refresh credentials, as in 2515 [RFC6749] Oauth 2.0, the server SHOULD tie the push subscription to 2516 the validity of the refresh token rather than the access token, and 2517 behave according to whether this is time-limited or not. 2519 7.2.1. PushSubscription/get 2521 Standard _/get_ method, except it does *not* take or return an 2522 _accountId_ argument, as push subscriptions are not tied to specific 2523 accounts. It also does *not* return a _state_ argument. The _ids_ 2524 argument may be "null" to fetch all at once. 2526 As the _url_ and _keys_ properties may contain data that is private 2527 to a particular device, the values for these properties MUST NOT be 2528 returned. If the _properties_ argument is "null" or omitted, the 2529 server MUST default to all properties excluding these two. If one of 2530 them is explicitly requested, the method call MUST be rejected with a 2531 "forbidden" error. 2533 7.2.2. PushSubscription/set 2535 Standard _/set_ method except it does *not* take or return an 2536 _accountId_ argument, as push subscriptions are not tied to specific 2537 accounts. It also does *not* take an _ifInState_ argument or return 2538 _oldState_ or _newState_ arguments. 2540 The _url_ and _keys_ properties are immutable; if the client wishes 2541 to change these, it must destroy the current push subscription and 2542 create a new one. 2544 The client may update the _expires_ property to extend (or, less 2545 commonly, shorten) the lifetime of a push subscription. The server 2546 MAY modify the proposed new expiry time to enforce server-defined 2547 limits. 2549 Clients SHOULD NOT update or destroy a push subscription that they 2550 did not create (i.e. has a _deviceClientId_ that they do not 2551 recognise). 2553 7.2.3. Example 2555 A client with deviceClientId "a889-ffea-910" fetches the set of push 2556 subscriptions currently on the server, making an API request with: 2558 [[ "PushSubscription/get", { 2559 "ids": null, 2560 }, "0" ]] 2562 Which returns: 2564 [[ "PushSubscription/get", { 2565 "list": [{ 2566 "id": "e50b2c1d-9553-41a3-b0a7-a7d26b599ee1", 2567 "deviceClientId": "b37ff8001ca0", 2568 "expires": "2018-01-31T00:13:21Z", 2569 "types": [ "Todo" ] 2570 }, { 2571 "id": "f2d0aab5-e976-4e8b-ad4b-b380a5b987e4", 2572 "deviceClientId": "8980f37f6c71", 2573 "expires": "2018-02-12T05:55:00Z", 2574 "types": [ "Mailbox", "Email", "EmailDelivery" ] 2575 }], 2576 "notFound": [] 2577 }, "0" ]] 2579 Since neither of the returned push subscription objects have the 2580 client's deviceClientId, it knows it does not have a current push 2581 subscription active on the server. So it creates one, sending this 2582 request: 2584 [[ "PushSubscription/set", { 2585 "create": { 2586 "4f29": { 2587 "deviceClientId": "a889-ffea-910", 2588 "url": "https://example.com/push/?device=8980f37f6c&client=12c6d086", 2589 "types": null 2590 } 2591 } 2592 }, "0" ]] 2594 The server creates the push subscription but limits the expiry time 2595 to 7 days in the future, returning this response: 2597 [[ "PushSubscription/set", { 2598 "created": { 2599 "4f29": { 2600 "id": "043dcfa4-1dd4-41ef-9156-2c89b3b19c60", 2601 "keys": null, 2602 "expires": "2018-07-13T02:14:29Z" 2603 } 2604 } 2605 }, "0" ]] 2607 Two days later, the client updates the subscription to extend its 2608 lifetime, sending this request: 2610 [[ "PushSubscription/set", { 2611 "update": { 2612 "043dcfa4-1dd4-41ef-9156-2c89b3b19c60": { 2613 "expires": "2018-08-13T00:00:00Z" 2614 } 2615 } 2616 }, "0" ]] 2618 The server extends the expiry time, but only again to its maximum 2619 limit of 7 days in the future, returning this response: 2621 [[ "PushSubscription/set", { 2622 "updated": { 2623 "043dcfa4-1dd4-41ef-9156-2c89b3b19c60": { 2624 "expires": "2018-07-16T02:22:50Z" 2625 } 2626 } 2627 }, "0" ]] 2629 7.3. Event Source 2631 Clients that can hold open TCP connections can connect directly to 2632 the JMAP server to receive push notifications via a "text/event- 2633 stream" resource, as described in . This is a long running HTTP request down which the 2635 server can push data. 2637 When a change occurs in the data on the server, it pushes an event 2638 called *state* to any connected clients, with the _StateChange_ 2639 object as the data. 2641 The server SHOULD also send a new event id that encodes the entire 2642 server state visible to the user immediately after sending a _state_ 2643 event. When a new connection is made to the event-source endpoint, a 2644 client following the server-sent events specification [1] will send a 2645 Last-Event-ID HTTP header with the last id it saw, which the server 2646 can use to work out whether the client has missed some changes. If 2647 so, it SHOULD send these changes immediately on connection. 2649 The client MAY add a query parameter called "types", with the value 2650 being a comma-separated list of type names. If present, the server 2651 MUST only push changes for the types in this list. If omitted, 2652 changes to all types are pushed. 2654 The client MAY add a query parameter called "closeafter" with value 2655 "state" to the event-source resource URL when requesting the event- 2656 source resource. If set, the server MUST end the HTTP response after 2657 pushing a _state_ event. This can be used by clients in environments 2658 where buffering proxies prevent the pushed data from arriving 2659 immediately, or indeed at all, when operating in the usual mode. 2661 The client MAY add a query parameter called "ping", with a positive 2662 integer value representing a length of time in seconds, e.g. 2663 "ping=300". If set, the server MUST send an event called *ping* 2664 whenever this time elapses since the previous event was sent. This 2665 MUST NOT set a new event id. 2667 The server MAY modify the interval given as a query parameter to be 2668 subject to a minimum and/or maximum value. For interoperability, 2669 servers MUST NOT have a minimum allowed value higher than 30 or a 2670 maximum allowed value less than 300. 2672 The data for the ping event MUST be a JSON object containing an 2673 _interval_ property, the value (type "PositiveInt") being the 2674 interval in seconds the server is using to send pings (this may be 2675 different to the requested value if the server clamped it to be 2676 within a min/max value). 2678 Clients can monitor for the _ping_ event to help determine when the 2679 closeafter mode may be required. 2681 Refer to the JMAP Session resource section of this spec for details 2682 on how to get the URL for the event-source resource. Requests to the 2683 resource MUST be authenticated. 2685 A client MAY hold open multiple connections to the event-source 2686 resource, although it SHOULD try to use a single connection for 2687 efficiency. 2689 8. Security considerations 2691 8.1. Transport confidentiality 2693 All HTTP requests MUST use [RFC5246] TLS (https) transport to ensure 2694 the confidentiality of data sent and received via JMAP. Clients MUST 2695 validate TLS certificate chains to protect against man-in-the-middle 2696 attacks. 2698 8.2. Authentication scheme 2700 A number of HTTP authentication schemes have been standardised 2701 (). Servers should take care to assess the security 2703 characteristics of different schemes in relation to their needs when 2704 deciding what to implement. 2706 If offering the Basic authentication scheme, services are strongly 2707 recommended to not allow a user's regular password but require 2708 generation of a unique "app password" via some external mechanism for 2709 each client they wish to connect. This allows connections from 2710 different devices to be differentiated by the server, and access to 2711 be individually revoked. 2713 8.3. Service autodiscovery 2715 Unless secured by something like DNSSEC, autodiscovery of server 2716 details is vulnerable to a DNS poisoning attack leading to the client 2717 talking to an attacker's server instead of the real JMAP server. The 2718 attacker may then man-in-the-middle requests and depending on the 2719 authentication scheme, steal credentials to generate its own 2720 requests. 2722 Clients that do not support SRV lookups are likely to try just using 2723 the "/.well-known/jmap" path directly against the domain of the 2724 username over HTTPS. Servers SHOULD ensure this path resolves or 2725 redirects to the correct JMAP Session resource to allow this to work. 2727 If this is not feasible, servers MUST ensure this path cannot be 2728 controlled by an attacker, as again it may be used to steal 2729 credentials. 2731 8.4. JSON parsing 2733 The security considerations of [RFC7159] apply to the use of JSON as 2734 the data interchange format. 2736 8.5. Denial of service 2738 A small request may result in a very large response, and require 2739 considerable work on the server if resource limits are not enforced. 2740 JMAP provides mechanisms for advertising and enforcing a wide variety 2741 of limits for mitigating this threat, including limits on number of 2742 objects fetched in a single method call, number of methods in a 2743 single request, number of concurrent requests, etc. 2745 JMAP servers MUST implement sensible limits to mitigate against 2746 resource exhaustion attacks. 2748 8.6. Push encryption 2750 When data changes, a small object is pushed with the new state 2751 strings for the types that have changed. While the data here is 2752 minimal, a passive man-in-the-middle attacker may be able to gain 2753 useful information. To ensure confidentiality, if the push is sent 2754 via a third party outside of the control of the client and JMAP 2755 server the client MUST specify encryption keys when establishing the 2756 PushSubscription. 2758 The privacy and security considerations of [RFC8030] and [RFC8291] 2759 also all apply to the use of the PushSubscription mechanism. 2761 9. IANA considerations 2763 9.1. Assignment of jmap service name 2765 IANA will assign the 'jmap' service name in the 'Service Name and 2766 Transport Protocol Port Number Registry' [RFC6335]. 2768 Service Name: jmap 2770 Transport Protocol(s): tcp 2772 Assignee: IESG 2774 Contact: IETF Chair 2775 Description: JSON Meta Application Protocol 2777 Reference: this document 2779 Assignment Notes: this service name was previously assigned under the 2780 name _JSON Mail Access Protocol_. This will be de-assigned and re- 2781 assigned with the approval of the previous assignee. 2783 9.2. Registration of well-known URI suffix for JMAP 2785 IANA will register the following well-known URI suffix for JMAP as 2786 described in [RFC5785]: 2788 URI Suffix: jmap 2790 Change Controller: IETF 2792 Specification Document: this document, section 2.2. 2794 9.3. Registration of the jmap URN sub-namespace 2796 IANA will register the following URN sub-namespace in the "IETF URN 2797 Sub-namespace for Registered Protocol Parameter Identifiers" registry 2798 as described in [RFC3553]. 2800 Registered Parameter Identifier: jmap 2802 Reference: this document, next section 2804 IANA Registry Reference: {insert IANA registry URL for registry in 2805 next section, upon approval} 2807 9.4. Creation of "JMAP Capabilities" registry 2809 IANA will create a registry for JMAP capabilities as described in 2810 section 2. JMAP capabilities are advertised in the _capabilities_ 2811 property of the _JMAP Session_ resource. They are used to extend the 2812 functionality of a JMAP server. A capability is referenced by a URI. 2813 The JMAP capability URI can be a URN starting with 2814 "urn:ietf:params:jmap:" plus a unique suffix which is the index value 2815 in the jmap URN sub-namespace. Registration of a JMAP capability 2816 with another form of URI has no impact on the jmap URN sub-namespace. 2818 This registry follows the expert review process unless the "intended 2819 use" field is _common_ or _placeholder_ in which case registration 2820 follows the specification required process. 2822 A JMAP capability registration can have an intended use of _common_, 2823 _placeholder_, _limited_, or _obsolete_. IANA will list common use 2824 registrations prominently and separately from those with other 2825 intended use values. 2827 The JMAP capability registration procedure is not a formal standards 2828 process, but rather an administrative procedure intended to allow 2829 community comment and sanity checking without excessive time delay. 2831 A _placeholder_ registration reserves part of the jmap urn namespace 2832 for another purpose but is typically not included in the 2833 _capabilities_ property of the _JMAP Session_ resource. 2835 9.4.1. Preliminary community review 2837 Notice of a potential JMAP common use registration SHOULD be sent to 2838 the jmap@ietf.org mailing list for review. This mailing list is 2839 appropriate to solicit community feedback on a proposed JMAP 2840 capability. Registrations that are not intended for common use MAY 2841 be sent to the list for review as well; doing so is entirely 2842 OPTIONAL, but is encouraged. 2844 The intent of the public posting to this list is to solicit comments 2845 and feedback on the choice of capability name, the unambiguity of the 2846 specification document, and a review of any interoperability or 2847 security considerations. The submitter may submit a revised 2848 registration proposal or abandon the registration completely and at 2849 any time. 2851 9.4.2. Submit request to IANA 2853 Registration requests can be sent to iana@iana.org. 2855 9.4.3. Designated expert review 2857 For a limited use registration, the designated expert's (DE) primary 2858 concern is preventing name collisions and encouraging the submitter 2859 to document security and privacy considerations; a published 2860 specification is not required. For a common use registration, the DE 2861 is expected to confirm that suitable documentation as described in 2862 [RFC8126], Section 4.6, is available. The DE should also verify the 2863 capability does not conflict with work that is active or already 2864 published within the IETF. 2866 Before a period of 30 days has passed, the DE will either approve or 2867 deny the registration request and publish a notice of the decision to 2868 the JMAP WG mailing list or its successor, as well as informing IANA. 2869 A denial notice must be justified by an explanation, and in the cases 2870 where it is possible, concrete suggestions on how the request can be 2871 modified so as to become acceptable should be provided. 2873 9.4.4. Change procedures 2875 Once a JMAP capability has been published by the IANA, the change 2876 controller may request a change to its definition. The same 2877 procedure that would be appropriate for the original registration 2878 request is used to process a change request. 2880 JMAP capability registrations may not be deleted; capabilities that 2881 are no longer believed appropriate for use can be declared obsolete 2882 by a change to their "intended use" field; such capabilities will be 2883 clearly marked in the lists published by the IANA. 2885 Significant changes to a capability's definition should be requested 2886 only when there are serious omissions or errors in the published 2887 specification. When review is required, a change request may be 2888 denied if it renders entities that were valid under the previous 2889 definition invalid under the new definition. 2891 The owner of a JMAP capability may pass responsibility to another 2892 person or agency by informing the IANA; this can be done without 2893 discussion or review. 2895 The IESG may reassign responsibility for a JMAP capability. The most 2896 common case of this will be to enable changes to be made to 2897 capabilities where the author of the registration has died, moved out 2898 of contact, or is otherwise unable to make changes that are important 2899 to the community. 2901 9.4.5. JMAP Capabilities registry template: 2903 Capability name: (see capability property in section 2) 2905 Specification document: 2907 Intended use: (one of common, limited, or obsolete) 2909 Change controller: (_IETF_ for standards-track/BCP RFCs) 2911 Security and privacy considerations: 2913 9.4.6. Initial registration for JMAP core 2915 Capability Name: "urn:ietf:params:jmap:core" 2917 Specification document: this document, section 2 2918 Intended use: common 2920 Change Controller: IETF 2922 Security and privacy considerations: this document, section 8. 2924 9.4.7. Registration for JMAP error placeholder in JMAP capabilities 2925 registry 2927 Capability Name: `urn:ietf:params:jmap:error:' 2929 Specification document: this document, next section. 2931 Intended use: placeholder 2933 Change Controller: IETF 2935 Security and privacy considerations: this document, section 8. 2937 9.5. Creation of "JMAP Error Codes" registry 2939 IANA will create a registry for JMAP error codes. JMAP error codes 2940 appear in the "type" member of a JSON problem details object (as 2941 described in section 3.5.1), in the "type" member in a JMAP error 2942 object (as described in section 3.5.2), or the "type" member of a 2943 JMAP method-specific error object (such as SetError in section 5.3). 2944 When used in a problem details object, the prefix 2945 'urn:ietf:params:jmap:error:' is always included, and when used in 2946 JMAP objects, the prefix is always omitted. 2948 This registry follows the expert review process. Preliminary 2949 community review for this registry follows the same procedures as the 2950 JMAP capabilities registry but is optional. The change procedures 2951 for this registry are the same as the change procedures for the JMAP 2952 capabilities registry. 2954 9.5.1. Designated expert review 2956 The designated expert should review the following aspects of the 2957 registration: 2959 1. Verify the error code does not conflict with existing names. 2961 2. Verify the error code follows the syntax limitations (does not 2962 require URI encoding). 2964 3. Encourage the error code to follow the naming convention of 2965 previously registered errors. 2967 4. Encourage description of client behaviors that are recommended in 2968 response to the error code. These may distinguish the error code 2969 from other error codes. 2971 5. Encourage description of when the server should issue the error 2972 as opposed to some other error code. 2974 6. Encourage the submitter to note any security considerations 2975 associated with the error, if any. For example, an error code 2976 that might disclose existence of data the authenticated user does 2977 not have permission to know about. 2979 Steps 3-6 are meant to promote a higher-quality registry. However, 2980 the expert is encouraged to approve any registration that would not 2981 actively harm JMAP interoperability to make this a relatively light- 2982 weight process. 2984 9.5.2. JMAP Error Codes registry template: 2986 JMAP Error Code: 2988 Intended use: (one of _common_, _limited_, _obsolete_) 2990 Change Controller: (_IETF_ for standards-track/BCP RFCs) 2992 Description or Reference: 2994 9.5.3. Initial JMAP Error Codes registry 2996 +------------------------------+---------+------------+-------------+ 2997 | JMAP Error Code | Intende | Change | Description | 2998 | | d Use | Controller | or | 2999 | | | | Reference | 3000 +------------------------------+---------+------------+-------------+ 3001 | accountNotFound | common | IETF | RFC XXXX | 3002 | | | | section | 3003 | | | | 3.5.2 | 3004 | accountNotSupportedByMethod | common | IETF | RFC XXXX | 3005 | | | | section | 3006 | | | | 3.5.2 | 3007 | accountReadOnly | common | IETF | RFC XXXX | 3008 | | | | section | 3009 | | | | 3.5.2 | 3010 | anchorNotFound | common | IETF | RFC XXXX | 3011 | | | | section 5.5 | 3012 | alreadyExists | common | IETF | RFC XXXX | 3013 | | | | section 5.4 | 3014 | cannotCalculateChanges | common | IETF | RFC XXXX | 3015 | | | | sections | 3016 | | | | 5.2 and 5.6 | 3017 | forbidden | common | IETF | RFC XXXX | 3018 | | | | sections | 3019 | | | | 3.5.2, 5.3, | 3020 | | | | and 7.2.1 | 3021 | fromAccountNotFound | common | IETF | RFC XXXX | 3022 | | | | sections | 3023 | | | | 5.4 and 6.3 | 3024 | fromAccountNotSupportedByMet | common | IETF | RFC XXXX | 3025 | hod | | | section 5.4 | 3026 | invalidArguments | common | IETF | RFC XXXX | 3027 | | | | section | 3028 | | | | 3.5.2 | 3029 | invalidPatch | common | IETF | RFC XXXX | 3030 | | | | section 5.3 | 3031 | invalidProperties | common | IETF | RFC XXXX | 3032 | | | | section 5.3 | 3033 | notFound | common | IETF | RFC XXXX | 3034 | | | | section 5.3 | 3035 | notJSON | common | IETF | RFC XXXX | 3036 | | | | section | 3037 | | | | 3.5.1 | 3038 | notRequest | common | IETF | RFC XXXX | 3039 | | | | section | 3040 | | | | 3.5.1 | 3041 | overQuota | common | IETF | RFC XXXX | 3042 | | | | section 5.3 | 3043 | rateLimit | common | IETF | RFC XXXX | 3044 | | | | section 5.3 | 3045 | requestTooLarge | common | IETF | RFC XXXX | 3046 | | | | sections | 3047 | | | | 5.1 and 5.3 | 3048 | invalidResultReference | common | IETF | RFC XXXX | 3049 | | | | section | 3050 | | | | 3.5.2 | 3051 | serverFail | common | IETF | RFC XXXX | 3052 | | | | section | 3053 | | | | 3.5.2 | 3054 | serverPartialFail | limited | IETF | RFC XXXX | 3055 | | | | section | 3056 | | | | 3.5.2 | 3057 | serverUnavailable | common | IETF | RFC XXXX | 3058 | | | | section | 3059 | | | | 3.5.2 | 3060 | singleton | common | IETF | RFC XXXX | 3061 | | | | section 5.3 | 3062 | stateMismatch | common | IETF | RFC XXXX | 3063 | | | | section 5.3 | 3064 | toAccountNotFound | common | IETF | RFC XXXX | 3065 | | | | sections | 3066 | | | | 5.4 and 6.3 | 3067 | toAccountNotSupportedByMetho | common | IETF | RFC XXXX | 3068 | d | | | section 5.4 | 3069 | tooLarge | common | IETF | RFC XXXX | 3070 | | | | section 5.3 | 3071 | tooManyChanges | common | IETF | RFC XXXX | 3072 | | | | section 5.6 | 3073 | unknownCapability | common | IETF | RFC XXXX | 3074 | | | | section | 3075 | | | | 3.5.1 | 3076 | unknownMethod | common | IETF | RFC XXXX | 3077 | | | | section | 3078 | | | | 3.5.2 | 3079 | unsupportedFilter | common | IETF | RFC XXXX | 3080 | | | | section 5.5 | 3081 | unsupportedSort | common | IETF | RFC XXXX | 3082 | | | | section 5.5 | 3083 | willDestroy | common | IETF | RFC XXXX | 3084 | | | | section 5.3 | 3085 +------------------------------+---------+------------+-------------+ 3087 10. References 3089 10.1. Normative References 3091 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 3092 Requirement Levels", BCP 14, RFC 2119, 3093 DOI 10.17487/RFC2119, March 1997, 3094 . 3096 [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for 3097 specifying the location of services (DNS SRV)", RFC 2782, 3098 DOI 10.17487/RFC2782, February 2000, 3099 . 3101 [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: 3102 Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, 3103 . 3105 [RFC3553] Mealling, M., Masinter, L., Hardie, T., and G. Klyne, "An 3106 IETF URN Sub-namespace for Registered Protocol 3107 Parameters", BCP 73, RFC 3553, DOI 10.17487/RFC3553, June 3108 2003, . 3110 [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 3111 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November 3112 2003, . 3114 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 3115 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 3116 . 3118 [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet 3119 Application Protocol Collation Registry", RFC 4790, 3120 DOI 10.17487/RFC4790, March 2007, 3121 . 3123 [RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation 3124 Algorithm", RFC 5051, DOI 10.17487/RFC5051, October 2007, 3125 . 3127 [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security 3128 (TLS) Protocol Version 1.2", RFC 5246, 3129 DOI 10.17487/RFC5246, August 2008, 3130 . 3132 [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, 3133 DOI 10.17487/RFC5322, October 2008, 3134 . 3136 [RFC5785] Nottingham, M. and E. Hammer-Lahav, "Defining Well-Known 3137 Uniform Resource Identifiers (URIs)", RFC 5785, 3138 DOI 10.17487/RFC5785, April 2010, 3139 . 3141 [RFC6186] Daboo, C., "Use of SRV Records for Locating Email 3142 Submission/Access Services", RFC 6186, 3143 DOI 10.17487/RFC6186, March 2011, 3144 . 3146 [RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S. 3147 Cheshire, "Internet Assigned Numbers Authority (IANA) 3148 Procedures for the Management of the Service Name and 3149 Transport Protocol Port Number Registry", BCP 165, 3150 RFC 6335, DOI 10.17487/RFC6335, August 2011, 3151 . 3153 [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., 3154 and D. Orchard, "URI Template", RFC 6570, 3155 DOI 10.17487/RFC6570, March 2012, 3156 . 3158 [RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", 3159 RFC 6749, DOI 10.17487/RFC6749, October 2012, 3160 . 3162 [RFC6764] Daboo, C., "Locating Services for Calendaring Extensions 3163 to WebDAV (CalDAV) and vCard Extensions to WebDAV 3164 (CardDAV)", RFC 6764, DOI 10.17487/RFC6764, February 2013, 3165 . 3167 [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type 3168 Specifications and Registration Procedures", BCP 13, 3169 RFC 6838, DOI 10.17487/RFC6838, January 2013, 3170 . 3172 [RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., 3173 "JavaScript Object Notation (JSON) Pointer", RFC 6901, 3174 DOI 10.17487/RFC6901, April 2013, 3175 . 3177 [RFC7159] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 3178 Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March 3179 2014, . 3181 [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 3182 Protocol (HTTP/1.1): Message Syntax and Routing", 3183 RFC 7230, DOI 10.17487/RFC7230, June 2014, 3184 . 3186 [RFC7235] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 3187 Protocol (HTTP/1.1): Authentication", RFC 7235, 3188 DOI 10.17487/RFC7235, June 2014, 3189 . 3191 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 3192 DOI 10.17487/RFC7493, March 2015, 3193 . 3195 [RFC7617] Reschke, J., "The 'Basic' HTTP Authentication Scheme", 3196 RFC 7617, DOI 10.17487/RFC7617, September 2015, 3197 . 3199 [RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP 3200 APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016, 3201 . 3203 [RFC8030] Thomson, M., Damaggio, E., and B. Raymor, Ed., "Generic 3204 Event Delivery Using HTTP Push", RFC 8030, 3205 DOI 10.17487/RFC8030, December 2016, 3206 . 3208 [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for 3209 Writing an IANA Considerations Section in RFCs", BCP 26, 3210 RFC 8126, DOI 10.17487/RFC8126, June 2017, 3211 . 3213 [RFC8291] Thomson, M., "Message Encryption for Web Push", RFC 8291, 3214 DOI 10.17487/RFC8291, November 2017, 3215 . 3217 10.2. URIs 3219 [1] https://html.spec.whatwg.org/multipage/server-sent-events.html 3221 Authors' Addresses 3223 Neil Jenkins 3224 FastMail 3225 PO Box 234, Collins St West 3226 Melbourne VIC 8007 3227 Australia 3229 Email: neilj@fastmailteam.com 3230 URI: https://www.fastmail.com 3232 Chris Newman 3233 Oracle 3234 440 E. Huntington Dr., Suite 400 3235 Arcadia CA 91006 3236 United States of America 3238 Email: chris.newman@oracle.com