idnits 2.17.1 draft-ietf-jmap-core-07.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 (August 7, 2018) is 2082 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 3153 ** 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: February 8, 2019 Oracle 6 August 7, 2018 8 JSON Meta Application Protocol 9 draft-ietf-jmap-core-07 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 February 8, 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 . . . . . . . . . . . . . . . . . . 13 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 . . . . . . . . . . . . . . . . . . . 15 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 . . . . . . . . . . . . . . . . 17 75 3.5.2. Method-level errors . . . . . . . . . . . . . . . . . 17 76 3.6. References to previous method results . . . . . . . . . . 19 77 3.7. Security . . . . . . . . . . . . . . . . . . . . . . . . 24 78 3.8. Concurrency . . . . . . . . . . . . . . . . . . . . . . . 24 79 4. The Core/echo method . . . . . . . . . . . . . . . . . . . . 24 80 4.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 24 81 5. Standard methods and naming convention . . . . . . . . . . . 25 82 5.1. /get . . . . . . . . . . . . . . . . . . . . . . . . . . 25 83 5.2. /changes . . . . . . . . . . . . . . . . . . . . . . . . 26 84 5.3. /set . . . . . . . . . . . . . . . . . . . . . . . . . . 29 85 5.4. /copy . . . . . . . . . . . . . . . . . . . . . . . . . . 34 86 5.5. /query . . . . . . . . . . . . . . . . . . . . . . . . . 36 87 5.6. /queryChanges . . . . . . . . . . . . . . . . . . . . . . 40 88 5.7. Examples . . . . . . . . . . . . . . . . . . . . . . . . 43 89 6. Binary data . . . . . . . . . . . . . . . . . . . . . . . . . 48 90 6.1. Uploading binary data . . . . . . . . . . . . . . . . . . 49 91 6.2. Downloading binary data . . . . . . . . . . . . . . . . . 50 92 6.3. Blob/copy . . . . . . . . . . . . . . . . . . . . . . . . 50 93 7. Push . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 94 7.1. The StateChange object . . . . . . . . . . . . . . . . . 51 95 7.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 52 96 7.2. PushSubscription . . . . . . . . . . . . . . . . . . . . 52 97 7.2.1. PushSubscription/get . . . . . . . . . . . . . . . . 54 98 7.2.2. PushSubscription/set . . . . . . . . . . . . . . . . 55 99 7.2.3. Example . . . . . . . . . . . . . . . . . . . . . . . 55 100 7.3. Event Source . . . . . . . . . . . . . . . . . . . . . . 57 101 8. Security considerations . . . . . . . . . . . . . . . . . . . 58 102 8.1. Transport confidentiality . . . . . . . . . . . . . . . . 58 103 8.2. Authentication scheme . . . . . . . . . . . . . . . . . . 58 104 8.3. Service autodiscovery . . . . . . . . . . . . . . . . . . 58 105 8.4. JSON parsing . . . . . . . . . . . . . . . . . . . . . . 59 106 8.5. Denial of service . . . . . . . . . . . . . . . . . . . . 59 107 8.6. Push encryption . . . . . . . . . . . . . . . . . . . . . 59 108 9. IANA considerations . . . . . . . . . . . . . . . . . . . . . 59 109 9.1. Assignment of jmap service name . . . . . . . . . . . . . 59 110 9.2. Registration of well-known URI suffix for JMAP . . . . . 60 111 9.3. Registration of the jmap URN sub-namespace . . . . . . . 60 112 9.4. Creation of "JMAP Capabilities" registry . . . . . . . . 60 113 9.4.1. Preliminary community review . . . . . . . . . . . . 61 114 9.4.2. Submit request to IANA . . . . . . . . . . . . . . . 61 115 9.4.3. Designated expert review . . . . . . . . . . . . . . 61 116 9.4.4. Change procedures . . . . . . . . . . . . . . . . . . 62 117 9.4.5. JMAP Capabilities registry template: . . . . . . . . 62 118 9.4.6. Initial registration for JMAP core . . . . . . . . . 62 119 9.4.7. Registration for JMAP error placeholder in JMAP 120 capabilities registry . . . . . . . . . . . . . . . . 63 121 9.5. Creation of "JMAP Error Codes" registry . . . . . . . . . 63 122 9.5.1. Designated expert review . . . . . . . . . . . . . . 63 123 9.5.2. JMAP Error Codes registry template: . . . . . . . . . 64 124 9.5.3. Initial JMAP Error Codes registry . . . . . . . . . . 64 125 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 66 126 10.1. Normative References . . . . . . . . . . . . . . . . . . 66 127 10.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 69 128 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 69 130 1. Introduction 132 JMAP is a generic protocol for synchronising data, such as mail, 133 calendars or contacts, between a client and a server. It is 134 optimised for mobile and web environments, and aims to provide a 135 consistent interface to different data types. 137 This specification is for the generic mechanism of data 138 synchronisation. Further specifications define the data models for 139 different data types that may be synchronised via JMAP. 141 JMAP is designed to make efficient use of limited network resources. 142 Multiple API calls may be batched in a single request to the server, 143 reducing round trips and improving battery life on mobile devices. 145 Push connections remove the need for polling, and an efficient delta 146 update mechanism ensures a minimum of data is transferred. 148 JMAP is designed to be horizontally scalable to a very large number 149 of users. This is facilitated by the separate end points for users 150 after login, the separation of binary and structured data, and a 151 shared data model that does not allow data dependencies between 152 accounts. 154 1.1. Notational conventions 156 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 157 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 158 document are to be interpreted as described in [RFC2119]. 160 The underlying format used for this specification is JSON. 161 Consequently, the terms "object" and "array" as well as the four 162 primitive types (strings, numbers, booleans, and null) are to be 163 interpreted as described in Section 1 of [RFC7159]. Unless otherwise 164 noted, all the property names and values are case sensitive. 166 Some examples in this document contain "partial" JSON documents used 167 for illustrative purposes. In these examples, three periods "..." 168 are used to indicate a portion of the document that has been removed 169 for compactness. 171 Unless otherwise specified, examples of API exchanges only show the 172 _methodCalls_ array of the Request object or the _methodResponses_ 173 array of the Response object. For compactness, the rest of the 174 Request/Response object is omitted. 176 Type signatures are given for all JSON values in this document. The 177 following conventions are used: 179 o "*" - The type is undefined (the value could be any type, although 180 permitted values may be constrained by the context of this value). 182 o "String" - The JSON string type. 184 o "Number" - The JSON number type. 186 o "Boolean" - The JSON boolean type. 188 o "String[A]" - A JSON object where the keys are all "String"s, and 189 the values are of type "A". 191 o "A[]" - An array of values of type "A". 193 o "A|B" - The value is either of type "A" or of type "B". 195 Other types may also be given, with their representation defined 196 elsewhere in this document. 198 1.2. The Int and PositiveInt data types 200 Where "Int" is given as a data type, it means an integer in the range 201 -2^53 <= value <= 2^53 (the maximum integer that may be reliably 202 stored in a floating-point double), represented as a JSON "Number". 204 Where "PositiveInt" is given as a data type, it means an "Int" where 205 the value MUST be in the range 0 <= value <= 2^53. 207 1.3. The Date and UTCDate data types 209 Where "Date" is given as a type, it means a string in [RFC3339] 210 _date-time_ format. To ensure a normalised form, the _time-secfrac_ 211 MUST always be omitted and any letters in the string (e.g. "T" and 212 "Z") MUST be upper-case. For example, ""2014-10-30T14:12:00+08:00"". 214 Where "UTCDate" is given as a type, it means a "Date" where the 215 _time-offset_ component MUST be "Z" (i.e. it must be in UTC time). 216 For example, ""2014-10-30T06:12:00Z"". 218 1.4. JSON as the data encoding format 220 JSON is a text-based data interchange format as specified in 221 [RFC7159]. The I-JSON format defined in [RFC7493] is a strict subset 222 of this, adding restrictions to avoid potentially confusing scenarios 223 (for example, it mandates that an object MUST NOT have two properties 224 with the same key). 226 All data sent from the client to the server or from the server to the 227 client (except binary file upload/download) MUST be valid I-JSON 228 according to the RFC, and is therefore case-sensitive and encoded in 229 UTF-8 ([RFC3629]). 231 1.5. Terminology 233 1.5.1. User 235 A user represents a set of permissions relating to what data can be 236 seen. 238 1.5.2. Accounts 240 An account is a collection of data. A single account may contain an 241 arbitrary set of data types, for example a collection of mail, 242 contacts and calendars. 244 All data belongs to a single account. With the exception of a few 245 explicit operations to copy data between accounts, all JMAP methods 246 take an _accountId_ argument that specifies on which account the 247 operations are to take place. This argument is always optional; if 248 not specified, the primary account for the capability that defines 249 the data type is used. (Though if there is no primary account for 250 that capability, an "accountNotFound" error will be returned.) 252 An account is not the same as a user, although it is common for a 253 primary account to directly belong to the user. For example, you may 254 have an account that contains data for a group or business, to which 255 multiple users have access. 257 A single set of credentials may provide access to multiple accounts, 258 for example if another user is sharing their mail with the logged in 259 user, or if there is a group account. 261 In the event of a severe internal error, a server may have to 262 reallocate ids or do something else that violates standard JMAP data 263 constraints for an account. In this situation, the data on the 264 server is no longer compatible with cached data the client may have 265 from before. The server MUST treat this as though the account has 266 been deleted and then recreated with a new account id. Clients will 267 then be forced to throw away any data with the old account id and 268 refetch all data from scratch. 270 1.5.3. Data types and records 272 JMAP provides a uniform interface for creating, retrieving, updating 273 and deleting various types of objects. A *data type* is a collection 274 of named, typed properties, just like the schema for a database 275 table. Each instance of a data type is called a *record*. 277 1.6. Ids 279 All record ids are assigned by the server, and are immutable. They 280 MUST be unique among all records of the *same type* within the *same 281 account*. Ids may clash across accounts, or for two records of 282 different types within the same account. 284 Ids are always "String"s. An id MUST be at least 1 character in 285 length and maximum 255 octets in size, and MUST only contain 286 characters from the "URL and Filename safe" Base 64 Alphabet, as 287 defined in section 5 of [RFC4648]. This is the ASCII alphanumeric 288 characters ("A-Za-z0-9"), hyphen ("-"), and underscore ("_"). 290 1.7. The JMAP API model 292 JMAP uses HTTP [RFC7230] to expose API, Push, Upload and Download 293 resources. Implementations MUST support HTTP/1.1, and MAY support 294 later versions. All HTTP requests MUST use [RFC5246] TLS (HTTPS) 295 transport. Support for common HTTP mechanisms such as redirection 296 and caching are assumed. 298 All HTTP requests MUST be authenticated. Servers MUST conform with 299 the [RFC7235] HTTP Authentication framework to reject requests that 300 fail authentication and inform the client of available authentication 301 schemes. 303 Clients SHOULD understand and be able to handle standard HTTP status 304 codes appropriately. 306 An authenticated client can fetch the JMAP Session object with 307 details about the data and capabilities the server can provide as 308 shown in section 2. The client may then exchange data with the 309 server in the following ways: 311 1. The client may make an API request to the server to get or set 312 structured data. This request consists of an ordered series of 313 method calls. These are processed by the server, which then 314 returns an ordered series of responses. This is described in 315 sections 3 and 4. 317 2. The client may download or upload binary files from/to the 318 server. This is detailed in section 5. 320 3. The client may connect to a push channel on the server, to be 321 notified when data has changed. This is explained in section 6. 323 1.8. Vendor-specific extensions 325 Individual services will have custom features they wish to expose 326 over JMAP. This may take the form of extra data types and/or methods 327 not in the spec, or extra arguments to JMAP methods, or extra 328 properties on existing data types (which may also appear in arguments 329 to methods that take property names). 331 The server can advertise custom extensions it supports by including 332 the identifiers in the capabilities object. Identifiers for vendor 333 extensions MUST be a URL belonging to a domain owned by the vendor, 334 to avoid conflict. The URL SHOULD resolve to documentation for the 335 changes the extension makes. 337 To ensure compatibility with clients that don't know about a specific 338 custom extension, and for compatibility with future versions of JMAP, 339 to use an extension the client MUST opt in by passing the appropriate 340 capability identifier in the _using_ array of the Request object, as 341 described in section 3.2. The server MUST only follow the 342 specifications that are opted-into and behave as though it does not 343 implement anything else when processing a request. 345 2. The JMAP Session resource 347 You need two things to connect to a JMAP server: 349 1. The URL for the JMAP Session resource. This may be requested 350 directly from the user, or discovered automatically based on a 351 username domain (see Service Autodiscovery section below). 353 2. Credentials to authenticate with. How to obtain credentials is 354 out of scope for this specification. 356 An authenticated GET request to the JMAP Session resource MUST return 357 the details about the data and capabilities the server can provide to 358 the client given those credentials. 360 The response to a successful request is a JSON-encoded *JMAP Session* 361 object. It has the following properties: 363 o *username*: "String" The username associated with the given 364 credentials. 366 o *accounts*: "String[Account]" A map of *account id* to Account 367 object for each account (see section 1.5.2) the user has access 368 to. An *Account* object has the following properties: 370 * *name*: "String" A user-friendly string to show when presenting 371 content from this account, e.g. the email address representing 372 the owner of the account. 374 * *isReadOnly*: "Boolean" This is "true" if the entire account is 375 read-only. 377 * *hasDataFor*: "String[]" A list of specification URIs for the 378 object types supported in this account. The server advertises 379 the list of specifications it supports in general in the 380 capabilities object, as defined below. If the specification 381 includes new object type definitions, the server MUST include 382 it the _hasDataFor_ array if, and only if, the user may use 383 those data types with this account. For example, you may have 384 access to your own account with mail, calendars and contacts 385 data, and also a shared account that only has contacts data (a 386 business address book for example). In this case the 387 _hasDataFor_ property on the first account would include 388 something like "urn:ietf:params:jmap:mail", 389 "urn:ietf:params:jmap:calendars", 390 "urn:ietf:params:jmap:contacts", while the second account would 391 just have the last of these. Attempts to use the methods 392 defined in a specification with one of the accounts that does 393 not contain those data types are rejected with an 394 _accountNotSupportedByMethod_ error (see the Method-level 395 errors section below). 397 o *primaryAccounts*: "String[String]" A map of capability URIs (as 398 found in _hasDataFor_) to the account id to be considered the 399 user's main or default account for data pertaining to that 400 capability. If no account being returned belongs to the user, or 401 in any other way there is no appropriate way to determine a 402 default account, there MAY be no entry for a particular data 403 profile name. "urn:ietf:params:jmap:core" SHOULD NOT be present. 405 o *capabilities*: "String[Object]" An object specifying the 406 capabilities of this server. Each key is a URI for a 407 specification supported by the server. The value for each of 408 these keys is an object with further information about the 409 server's capabilities in relation to that specification. The 410 client MUST ignore any properties it does not understand. The 411 capabilities object MUST include a property called 412 "urn:ietf:params:jmap:core". The value of this property is an 413 object which MUST contain the following information on server 414 capabilities: 416 * *maxSizeUpload*: "PositiveInt" The maximum file size, in 417 octets, that the server will accept for a single file upload 418 (for any purpose). 420 * *maxConcurrentUpload*: "PositiveInt" The maximum number of 421 concurrent requests the server will accept to the upload 422 endpoint. 424 * *maxSizeRequest*: "PositiveInt" The maximum size, in octets, 425 that the server will accept for a single request to the API 426 endpoint. 428 * *maxConcurrentRequests*: "PositiveInt" The maximum number of 429 concurrent requests the server will accept to the API endpoint. 431 * *maxCallsInRequest*: "PositiveInt" The maximum number of method 432 calls the server will accept in a single request to the API 433 endpoint. This MUST be greater than or equal to "32" to ensure 434 clients can rely on the ability to make efficient network use. 436 * *maxObjectsInGet*: "PositiveInt" The maximum number of objects 437 that the client may request in a single "/get" type method 438 call. 440 * *maxObjectsInSet*: "PositiveInt" The maximum number of objects 441 the client may send to create, update or destroy in a single 442 "/set" type method call. 444 * *collationAlgorithms*: "String[]" A list of identifiers for 445 algorithms registered in the collation registry defined in 446 [RFC4790] that the server supports for sorting when querying 447 records. 449 Future specifications will define their own properties on the 450 capabilities object. Servers MAY advertise vendor-specific JMAP 451 extensions, as described in section 1.8. To avoid conflict, the 452 identifiers for these MUST be a URL with a domain owned by the 453 vendor. Clients MUST opt in to any specifications it wishes to 454 use (see section 3.1). 456 o *apiUrl*: "String" The URL to use for JMAP API requests. 458 o *downloadUrl*: "String" The URL endpoint to use when downloading 459 files in [RFC6570] URI Template (level 1) format. The URL MUST 460 contain variables called "accountId", "blobId", "type" and "name". 461 The use of these variables is described in section 5.2. Due to 462 potential encoding issues with slashes in content types, it is 463 recommended to put the "type" variable in the query section of the 464 URL. 466 o *uploadUrl*: "String" The URL endpoint to use when uploading files 467 in [RFC6570] URI Template (level 1) format. The URL MUST contain 468 a variable called "accountId". The use of this variable is 469 described in section 5.1. 471 o *eventSourceUrl*: "String" The URL to connect to for push events, 472 as described in section 6.3. 474 To ensure future compatibility, other properties MAY be included on 475 the JMAP Session object. Clients MUST ignore any properties they are 476 not expecting. 478 2.1. Example 480 In the following example JMAP Session object, the user has access to 481 his own mail and contacts via JMAP, as well as read-only access to 482 shared mail from another user. The server is advertising a custom 483 "https://example.com/apis/foobar" capability. 485 { 486 "username": "john@example.com", 487 "accounts": { 488 "13824": { 489 "name": "john@example.com", 490 "isReadOnly": false, 491 "hasDataFor": [ 492 "urn:ietf:params:jmap:mail", 493 "urn:ietf:params:jmap:contacts" 494 ] 495 }, 496 "97813": { 497 "name": "jane@example.com", 498 "isReadOnly": true, 499 "hasDataFor": [ "urn:ietf:params:jmap:mail" ] 500 } 501 }, 502 "primaryAccounts": { 503 "urn:ietf:params:jmap:mail": "13824", 504 "urn:ietf:params:jmap:contacts": "13824" 505 }, 506 "capabilities": { 507 "urn:ietf:params:jmap:core": { 508 "maxSizeUpload": 50000000, 509 "maxConcurrentUpload": 8, 510 "maxSizeRequest": 10000000, 511 "maxConcurrentRequest": 8, 512 "maxCallsInRequest": 32, 513 "maxObjectsInGet": 256, 514 "maxObjectsInSet": 128, 515 "collationAlgorithms": [ 516 "i;ascii-numeric", 517 "i;ascii-casemap", 518 "i;unicode-casemap" 519 ] 520 }, 521 "https://example.com/apis/foobar": { 522 "maxFoosFinangled": 42 523 }, 524 ... 525 }, 526 "apiUrl": "https://jmap.example.com/api/", 527 "downloadUrl": "https://jmap.example.com/download/{accountId}/{blobId}/{name}?accept={type}", 528 "uploadUrl": "https://jmap.example.com/upload/{accountId}/", 529 "eventSourceUrl": "https://jmap.example.com/eventsource/" 530 } 531 2.2. Service Autodiscovery 533 There are two standardised autodiscovery methods in use for internet 534 protocols: 536 o *DNS SRV* ([RFC2782], [RFC6186] and [RFC6764]) 538 o *.well-known/servicename* ([RFC5785]) 540 A JMAP-supporting host for the domain "example.com" SHOULD publish a 541 SRV record "_jmap._tcp.example.com" which gives a _hostname_ and 542 _port_ (usually port "443"). The JMAP Session resource is then 543 "https://${hostname}[:${port}]/.well-known/jmap" (following any 544 redirects). 546 If the client has a username in the form of an email address, it MAY 547 use the domain portion of this to attempt autodiscovery of the JMAP 548 server. 550 3. Structured data exchange 552 The client may make an API request to the server to get or set 553 structured data. This request consists of an ordered series of 554 method calls. These are processed by the server, which then returns 555 an ordered series of responses. 557 3.1. Making an API request 559 To make an API request, the client makes an authenticated POST 560 request to the API resource, which is defined by the _apiUrl_ 561 property in the JMAP Session object. 563 The request MUST be of type "application/json" and consist of a 564 single JSON *Request* object. If successful, the response MUST also 565 be of type "application/json" and consist of a single *Response* 566 object. 568 3.2. The Request object 570 A *Request* object has the following properties: 572 o *using*: "String[]" The set of capabilities the client wishes to 573 use. The client MAY include capability identifiers even if the 574 method calls it makes do not utilise those capabilities. The 575 server advertises the set of specifications it supports in the 576 JMAP Session object, as keys on the _capabilities_ property. 578 o *methodCalls*: "Array[]" An array of method calls to process on 579 the server. The method calls MUST be processed sequentially, in 580 order. A *method call* is represented by an array containing 581 three elements: 583 1. A "String" *name* of the method to call. 585 2. An "String[*]" object containing _named_ *arguments* for that 586 method. 588 3. A *client id*: an arbitrary "String" to be echoed back with 589 the responses emitted by that method call (a method may return 590 1 or more responses, as it may make implicit calls to other 591 methods; all responses initiated by this method call get the 592 same client id in the response). 594 o *createdIds*: "String[String]" (optional) A map of (client- 595 specified) creation id to the id the server assigned when a record 596 was successfully created. As described later in this 597 specification, some records may have a property that contains the 598 id of another record. To allow more efficient network usage, you 599 can set this property to reference a record created earlier in the 600 same API request. Since the real id is unknown when the request 601 is created, the client can instead specify the creation id it 602 assigned, prefixed with a "#". As the server processes API 603 requests, any time it successfully creates a new record it adds to 604 this map the creation id, with the server-assigned real id as the 605 value. If it comes across a reference to a creation id in a 606 create/update, it looks it up in the map and replaces the 607 reference with the real id, if found. The client can pass an 608 initial value for this map as the _createdIds_ property of the 609 Request. This may be an empty object. If given in the request, 610 the response will also include a createdIds property, with any 611 additionally created ids added. This allows proxy servers to 612 easily split a JMAP request into multiple JMAP requests to send to 613 different servers. For example it could send the first two method 614 calls to server A, then the third to server B, before sending the 615 fourth to server A again. By passing the createdIds of the 616 previous response to the next request, it can ensure all of these 617 still resolve. 619 Future specifications MAY add further properties to the Request 620 object to extend the semantics. To ensure forwards compatibility, a 621 server MUST ignore any other properties it does not understand on the 622 JMAP request object. 624 3.2.1. Example request 626 { 627 "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], 628 "methodCalls": [ 629 [ "method1", { 630 "arg1": "arg1data", 631 "arg2": "arg2data" 632 }, "c1" ], 633 [ "method2", { 634 "arg1": "arg1data" 635 }, "c2" ], 636 [ "method3", {}, "c3" ] 637 ] 638 } 640 3.3. The Response object 642 A *Response* object has the following properties: 644 o *methodResponses*: "Array[]" An array of responses, in the same 645 format as the _methodCalls_ on the request object. The output of 646 the methods MUST be added to the _methodResponses_ array in the 647 same order as the methods are processed. 649 o *createdIds*: "String[String]" (optional; only returned if given 650 in request) A map of (client-specified) creation id to the id the 651 server assigned when a record was successfully created. This 652 includes all values passed in the request, as well as any 653 additional ones added for newly created records. 655 Unless otherwise specified, if the method call completed successfully 656 its response name is the same as the method name in the request. 658 3.3.1. Example response: 660 { 661 "methodResponses": [ 662 [ "method1", { 663 "arg1": 3, 664 "arg2": "foo" 665 }, "c1" ], 666 [ "method2", { 667 "isBlah": true 668 }, "c2" ], 669 [ "anotherResponseFromMethod2", { 670 "data": 10, 671 "yetmoredata": "Hello" 672 }, "c2"], 673 [ "error", { 674 "type":"unknownMethod" 675 }, "c3" ] 676 ] 677 } 679 3.4. Omitting arguments 681 An argument to a method may be specified to have a default value. If 682 omitted by the client, the server MUST treat the method call the same 683 as if the default value had been specified. Similarly, the server 684 MAY omit any argument in a response which has the default value. 686 Unless otherwise specified in a method description, "null" is the 687 default value for any argument in a request or response where this is 688 allowed by the type signature. Other arguments may only be omitted 689 if an explicit default value is defined in the method description. 691 3.5. Errors 693 There are three different levels of granularity at which an error may 694 be returned in JMAP. 696 When an API request is made, the request as a whole may be rejected 697 due to rate limiting, malformed JSON, request for an unknown 698 capability etc. In this case the entire request is rejected with an 699 appropriate HTTP error response code, and an additional JSON body 700 with more detail for the client. 702 Provided the request itself is syntactically valid, the methods 703 within it are executed sequentially by the server. Each method may 704 individually fail, for example if invalid arguments are given, or an 705 unknown method name is called. 707 Finally, methods that make changes to the server state often act upon 708 a number of different records within a single call. Each record 709 change may be separately rejected with a SetError, as described in 710 section 5.3. 712 3.5.1. Request-level errors 714 When an HTTP error response is returned to the client, the server 715 SHOULD return a JSON "problem details" object as the response body, 716 as per [RFC7807]. 718 The following problem types are defined: 720 o "urn:ietf:params:jmap:error:unknownCapability" The client included 721 a capability in the "using" property of the request that the 722 server does not support. 724 o "urn:ietf:params:jmap:error:notJSON" The content type of the 725 request was not "application/json" or the request did not parse as 726 I-JSON. 728 o "urn:ietf:params:jmap:error:notRequest" The request parsed as JSON 729 but did not match the structure of the Request object. 731 o "urn:ietf:params:jmap:error:limit" The request was not processed 732 as it would have exceeded one of the *request* limits defined on 733 the capability object, such as maxSizeRequest, maxCallsInRequest 734 or maxConcurrentRequests. A "limit" property MUST also be present 735 on the "problem details" object, containing the name of the limit 736 being applied. 738 3.5.1.1. Example 740 { 741 "type": "urn:ietf:params:jmap:error:unknownCapability", 742 "status": 400, 743 "detail": "The request object used capability 'https://example.com/apis/foobar', which is not supported by this server." 744 } 746 3.5.2. Method-level errors 748 If a method encounters an error, the appropriate "error" response 749 MUST be inserted at the current point in the _methodResponses_ array 750 and, unless otherwise specified, further processing MUST NOT happen 751 within that method call. 753 Any further method calls in the request MUST then be processed as 754 normal. Errors at the method level MUST NOT generate an HTTP-level 755 error. 757 An "error" response looks like this: 759 [ "error", { 760 "type": "unknownMethod" 761 }, "client-id" ] 763 The response name is "error", and it MUST have a type property. 764 Other properties may be present with further information; these are 765 detailed in the error type descriptions where appropriate. 767 With the exception of "serverPartialFail", the externally-visible 768 state of the server MUST NOT have changed if an error is returned at 769 the method level. 771 The following error types are defined which may be returned for any 772 method call where appropriate: 774 "serverUnavailable": Some internal server resource was temporarily 775 unavailable. Attempting the same operation later (perhaps after a 776 backoff with a random factor) may succeed. 778 "serverFail": An unexpected or unknown error occurred during the 779 processing of the call. A _description_ property should provide more 780 details about the error. The method call made no changes to the 781 server's state. Attempting the same operation again is expected to 782 fail again. Contacting the service administrator is likely necessary 783 to resolve this problem if it is persistent. 785 "serverPartialFail": Some, but not all expected changes described by 786 the method occurred. The client MUST re-synchronise impacted data to 787 determine server state. Use of this error is strongly discouraged. 789 "unknownMethod": The server does not recognise this method name. 791 "invalidArguments": One of the arguments is of the wrong type or 792 otherwise invalid, or a required argument is missing. A 793 "description" property MAY be present to help debug with an 794 explanation of what the problem was. This is a non-localised string, 795 and is not intended to be shown directly to end users. 797 "invalidResultReference": The method used a back reference for one of 798 its arguments (see the next section), but this failed to resolve. 800 "forbidden": The method and arguments are valid, but executing the 801 method would violate an ACL or other permissions policy. 803 "accountNotFound": An _accountId_ was included with the method call 804 that does not correspond to a valid account, or _accountId_ was null 805 but there is no primary account for the capability that defines the 806 data type. 808 "accountNotSupportedByMethod": An _accountId_ given corresponds to a 809 valid account, but the account does not support this data type. 811 "accountReadOnly": This method call would modify state in an account 812 that has "isReadOnly == true". 814 Further possible errors for a particular method are specified in the 815 method descriptions. 817 Further general errors MAY be defined in future RFCs. Should a 818 client receive an error type it does not understand, it MUST treat it 819 the same as the "serverFail" type. 821 3.6. References to previous method results 823 To allow clients to make more efficient use of the network and avoid 824 round trips, an argument to one method can be taken from the result 825 of a previous method call. 827 To do this, the client prefixes the argument name with "#". The 828 value is a _ResultReference_ object as described below. When 829 processing a method call, the server MUST first check the arguments 830 object for any names beginning with "#". If found, the back 831 reference should be resolved and the value used as the "real" 832 argument. The method is then processed as normal. If any back 833 reference fails to resolve, the whole method MUST be rejected with an 834 "invalidResultReference" error. If an argument object contains the 835 same argument name in normal and referenced form (e.g. "foo" and 836 "#foo"), the method MUST return an "invalidArguments" error. 838 A *ResultReference* object has the following properties: 840 o *resultOf*: "String" The client id of the method call to get the 841 result from (the string given as the third item in the array for a 842 method call). 844 o *name*: "String" The expected name of the response. 846 o *path*: "String" A pointer into the arguments. This is an 847 [RFC6901] JSON Pointer, except it also allows the use of "*" to 848 map through an array (see description below). 850 To resolve: 852 1. Find the first response with a client id identical to the 853 _resultOf_ property of the _ResultReference_ in the 854 _methodResponses_ array from previously processed method calls in 855 the same request. If none, evaluation fails. 857 2. If the response name is not identical to the _name_ property of 858 the _ResultReference_, evaluation fails. 860 3. Apply the _path_ to the arguments object of the response (the 861 second item in the response array) following the [RFC6901] JSON 862 Pointer algorithm, except with the following addition in 863 Section 4 (Evaluation): 865 If the currently referenced value is a JSON array, the reference 866 token may be exactly the single character "*", making the new 867 referenced value the result of applying the rest of the JSON pointer 868 tokens to every item in the array and returning the results in the 869 same order in a new array. If the result of applying the rest of the 870 pointer tokens to a value was itself an array, its items should be 871 included individually in the output rather than including the array 872 itself (i.e. the result is flattened from an array of arrays to a 873 single array). 875 As a simple example, suppose we have the following API request 876 _methodCalls_: 878 [[ "Foo/changes", { 879 "sinceState": "abcdef" 880 }, "t0" ], 881 [ "Foo/get", { 882 "#ids": { 883 "resultOf": "t0", 884 "name": "Foo/changes", 885 "path": "/created" 886 } 887 }, "t1" ]] 889 After executing the first method call the _methodResponses_ array is: 891 [[ "Foo/changes", { 892 "accountId": "1", 893 "oldState": "abcdef", 894 "newState": "123456", 895 "hasMoreChanges": false, 896 "created": [ "f1", "f4" ], 897 "updated": [], 898 "destroyed": [] 899 }, "t0" ]] 901 So to execute the Foo/get call, we look through the arguments and 902 find there is one with a "#" prefix. To resolve this, we apply the 903 algorithm above: 905 1. Find the first response with client id "t0". The Foo/changes 906 response fulfils this criterion. 908 2. Check the response name is the same as in the result reference. 909 It is, so this is fine. 911 3. Apply the _path_ as a JSON pointer to the arguments object. This 912 simply selects the "created" property, so the result of 913 evaluating is: "[ "f1", "f4" ]" 915 The JMAP server now continues to process the Foo/get call as though 916 the arguments were: 918 { 919 "ids": [ "f1", "f4" ] 920 } 922 Now a more complicated example using the JMAP Mail data model: fetch 923 the "from"/"date"/"subject" for every email in the first 10 threads 924 in the Inbox (sorted newest first): 926 [[ "Email/query", { 927 "filter": { "inMailbox": "id_of_inbox" }, 928 "sort": [{ "property": "receivedAt", "isAscending": false }], 929 "collapseThreads": true, 930 "position": 0, 931 "limit": 10, 932 "calculateTotal": true 933 }, "t0" ], 934 [ "Email/get", { 935 "#ids": { 936 "resultOf": "t0", 937 "name": "Email/query", 938 "path": "/ids" 939 }, 940 "properties": [ "threadId" ] 941 }, "t1" ], 942 [ "Thread/get", { 943 "#ids": { 944 "resultOf": "t1", 945 "name": "Email/get", 946 "path": "/list/*/threadId" 947 } 948 }, "t2" ], 949 [ "Email/get", { 950 "#ids": { 951 "resultOf": "t2", 952 "name": "Thread/get", 953 "path": "/list/*/emailIds" 954 }, 955 "properties": [ "from", "receivedAt", "subject" ] 956 }, "t3" ]] 958 After executing the first 3 method calls the _methodResponses_ array 959 might be: 961 [[ "Email/query", { 962 "accountId": "1", 963 "filter": { "inMailbox": "id_of_inbox" }, 964 "sort": [{ "property": "receivedAt", "isAscending": false }], 965 "collapseThreads": true, 966 "queryState": "abcdefg", 967 "canCalculateChanges": true, 968 "position": 0, 969 "total": 101, 970 "ids": [ "msg1023", "msg223", "msg110", "msg93", "msg91", "msg38", "msg36", "msg33", "msg11", "msg1" ] 971 }, "t0" ], 972 [ "Email/get", { 973 "accountId": "1", 974 "state": "123456", 975 "list": [{ 976 "id": "msg1023", 977 "threadId": "trd194", 978 }, { 979 "id": "msg223", 980 "threadId": "trd114" 981 }, 982 ... 983 ], 984 "notFound": [] 985 }, "t1" ], 986 [ "Thread/get", { 987 "accountId": "1", 988 "state": "123456", 989 "list": [{ 990 "id: "trd194", 991 "emailIds": [ "msg1020", "msg1021", "msg1023" ] 992 }, { 993 "id: "trd114", 994 "emailIds": [ "msg201", "msg223" ] 995 }, 996 ... 997 ], 998 "notFound": [] 999 }, "t2" ]] 1001 So to execute the final Email/get call, we look through the arguments 1002 and find there is one with a "#" prefix. To resolve this, we apply 1003 the algorithm: 1005 1. Find the first response with client id "t2". The "Thread/get" 1006 response fulfils this criterion. 1008 2. "Thread/get" is the name specified in the result reference, so 1009 this is fine. 1011 3. Apply the _path_ as a JSON pointer to the arguments object. 1012 Token-by-token: a) "list": get the array of thread objects b) 1013 "*": for each of the items in the array: 1015 i) `emailIds`: get the array of email ids 1016 ii) Concatenate these into a single array of all the ids in the result. 1018 The JMAP server now continues to process the Email/get call as though 1019 the arguments were: 1021 { 1022 "ids": [ "msg1020", "msg1021", "msg1023", "msg201", "msg223", ... ], 1023 "properties": [ "from", "receivedAt", "subject" ] 1024 } 1026 3.7. Security 1028 As always, the server must be strict about data received from the 1029 client. Arguments need to be checked for validity; a malicious user 1030 could attempt to find an exploit through the API. In case of invalid 1031 arguments (unknown/insufficient/wrong type for data etc.) the method 1032 MUST return an "invalidArguments" error and terminate. 1034 3.8. Concurrency 1036 Method calls within a single request MUST be executed in order. 1037 However, method calls from different concurrent API requests may be 1038 interleaved. This means that the data on the server may change 1039 between two method calls within a single API request. 1041 4. The Core/echo method 1043 The _Core/echo_ method returns exactly the same arguments as it is 1044 given. It is useful for testing you have a valid authenticated 1045 connection to a JMAP API endpoint. 1047 4.1. Example 1049 Request: 1051 [[ "Core/echo", { 1052 "hello": true, 1053 "high": 5 1054 }, "b3ff" ]] 1056 Response: 1058 [[ "Core/echo", { 1059 "hello": true, 1060 "high": 5 1061 }, "b3ff" ]] 1063 5. Standard methods and naming convention 1065 JMAP provides a uniform interface for creating, retrieving, updating 1066 and deleting objects of a particular type. For a "Foo" data type, 1067 records of that type would be fetched via a Foo/get call and modified 1068 via a Foo/set call. Delta updates may be fetched via a Foo/changes 1069 call. These methods all follow a standard format as described below. 1071 Not all types may have all methods. Specifications defining types 1072 MUST specify which methods are available for the type. 1074 5.1. /get 1076 Objects of type *Foo* are fetched via a call to _Foo/get_. 1078 It takes the following arguments: 1080 o *accountId*: "String|null" The id of the Account to use. If 1081 "null", the primary account for the capability that defines the 1082 data type is used. 1084 o *ids*: "String[]|null" The ids of the Foo objects to return. If 1085 "null" then *all* records of the data type are returned, if this 1086 is supported for that data type. 1088 o *properties*: "String[]|null" If supplied, only the properties 1089 listed in the array are returned for each Foo object. If "null", 1090 all properties of the object are returned. The id of the object 1091 is *always* returned, even if not explicitly requested. If an 1092 invalid property is requested, the call MUST be rejected with an 1093 "invalidArguments" error. 1095 The response has the following arguments: 1097 o *accountId*: "String" The id of the account used for the call. 1099 o *state*: "String" A string representing the state on the server 1100 for *all* the data of this type in the account (not just the 1101 objects returned in this call). If the data changes, this string 1102 MUST change. If the Foo data is unchanged, servers SHOULD return 1103 the same state string on subsequent requests for this data type. 1105 When a client receives a response with a different state string to 1106 a previous call, it MUST either throw away all currently cached 1107 objects for the type, or call _Foo/changes_ to get the exact 1108 changes. 1110 o *list*: "Foo[]" An array of the Foo objects requested. This is 1111 the *empty array* if no objects were found, or if the _ids_ 1112 argument passed in was also the empty array. The results MAY be 1113 in a different order to the _ids_ in the request arguments. If an 1114 identical id is included more than once in the request, the server 1115 MUST only include it once in either the _list_ or _notFound_ 1116 argument of the response. 1118 o *notFound*: "String[]" This array contains the ids passed to the 1119 method for records that do not exist. The array is empty if all 1120 requested ids were found, or if the _ids_ argument passed in was 1121 either "null" or the empty array. 1123 The following additional error may be returned instead of the _Foo/ 1124 get_ response: 1126 "requestTooLarge": The number of _ids_ requested by the client 1127 exceeds the maximum number the server is willing to process in a 1128 single method call. 1130 5.2. /changes 1132 When the state of the set of Foo records changes on the server 1133 (whether due to creation, updates or deletion), the _state_ property 1134 of the _Foo/get_ response will change. The _Foo/changes_ method 1135 allows a client to efficiently update the state of its Foo cache to 1136 match the new state on the server. It takes the following arguments: 1138 o *accountId*: "String|null" The id of the Account to use. If 1139 "null", the primary account for the capability that defines the 1140 data type is used. 1142 o *sinceState*: "String" The current state of the client. This is 1143 the string that was returned as the _state_ argument in the _Foo/ 1144 get_ response. The server will return the changes that have 1145 occurred since this state. 1147 o *maxChanges*: "PositiveInt|null" The maximum number of ids to 1148 return in the response. The server MAY choose to return fewer 1149 than this value, but MUST NOT return more. If not given by the 1150 client, the server may choose how many to return. If supplied by 1151 the client, the value MUST be a positive integer greater than 0. 1153 If a value outside of this range is given, the server MUST reject 1154 the call with an "invalidArguments" error. 1156 The response has the following arguments: 1158 o *accountId*: "String" The id of the account used for the call. 1160 o *oldState*: "String" This is the _sinceState_ argument echoed 1161 back; the state from which the server is returning changes. 1163 o *newState*: "String" This is the state the client will be in after 1164 applying the set of changes to the old state. 1166 o *hasMoreChanges*: "Boolean" If "true", the client may call _Foo/ 1167 changes_ again with the _newState_ returned to get further 1168 updates. If "false", _newState_ is the current server state. 1170 o *created*: "String[]" An array of ids for records which have been 1171 created since the old state. 1173 o *updated*: "String[]" An array of ids for records which have been 1174 updated since the old state. 1176 o *destroyed*: "String[]" An array of ids for records which have 1177 been destroyed since the old state. 1179 If a record has been created AND updated since the old state, the 1180 server SHOULD just return the id in the _created_ list, but MAY 1181 return it in the _updated_ list as well. 1183 If a record has been updated AND destroyed since the old state, the 1184 server SHOULD just return the id in the _destroyed_ list, but MAY 1185 return it in the _updated_ list as well. 1187 If a record has been created AND destroyed since the old state, the 1188 server SHOULD remove the id from the response entirely, but MAY 1189 include it in the _destroyed_ list, and if so MAY also include it in 1190 the _created_ list. 1192 If a _maxChanges_ is supplied, or set automatically by the server, 1193 the server MUST ensure the number of ids returned across _created_, 1194 _updated_ and _destroyed_ does not exceed this limit. If there are 1195 more changes than this between the client's state and the current 1196 server state, the server SHOULD generate an update to take the client 1197 to an intermediate state, from which the client can continue to call 1198 _Foo/changes_ until it is fully up to date. If it is unable to 1199 calculate an intermediate state, it MUST return a 1200 "cannotCalculateChanges" error response instead. 1202 When generating intermediate states, the server may choose how to 1203 divide up the changes. For many types it will provide a better user 1204 experience to return the more recent changes first, as this is more 1205 likely to be what the user is most interested in. The client can 1206 then continue to page in the older changes while the user is viewing 1207 the newer data. For example, suppose a server went through the 1208 following states: 1210 A -> B -> C -> D -> E 1212 And a client asks for changes from state "B". The server might first 1213 get the ids of records created, updated or destroyed between states D 1214 and E, returning them with: 1216 state: "B-D-E" 1217 hasMoreChanges: true 1219 The client will then ask for the change from state "B-D-E", and the 1220 server can return the changes between states C and D, returning: 1222 state: "B-C-E" 1223 hasMoreChanges: true 1225 Finally the client will request the changes from "B-C-E" and the 1226 server can return the changes between states B and C, returning: 1228 state: "E" 1229 hasMoreChanges: false 1231 Should the state on the server be modified in the middle of all this 1232 (to "F"), the server still does the same but now when the update to 1233 state "E" is returned, it would indicate that it still has more 1234 changes for the client to fetch. 1236 Where multiple changes to a record are split across different 1237 intermediate states, the server MUST NOT return a record as created 1238 in a later response than one which gives it as updated or destroyed, 1239 and MUST NOT return a record as destroyed before a response that 1240 gives it as created or updated. The server may have to coalesce 1241 multiple changes to a record to satisfy this requirement. 1243 The following additional errors may be returned instead of the _Foo/ 1244 changes_ response: 1246 "cannotCalculateChanges": The server cannot calculate the changes 1247 from the state string given by the client. Usually due to the 1248 client's state being too old, or the server being unable to produce 1249 an update to an intermediate state when there are too many updates. 1250 The client MUST invalidate its Foo cache. 1252 Maintaining state to allow calculation of _Foo/changes_ can be 1253 expensive for the server, but always returning 1254 _cannotCalculateChanges_ severely increases network traffic and 1255 resource usage for the client. To allow efficient sync, servers 1256 SHOULD be able to calculate changes from any state string that was 1257 given to a client within the last 30 days (but of course may support 1258 calculating updates from states older than this). 1260 5.3. /set 1262 Modifying the state of Foo objects on the server is done via the 1263 _Foo/set_ method. This encompasses creating, updating and destroying 1264 Foo records. This allows the server to sort out ordering and 1265 dependencies that may exist if doing multiple operations at once (for 1266 example to ensure there is always a minimum number of a certain 1267 record type). 1269 The _Foo/set_ method takes the following arguments: 1271 o *accountId*: "String|null" The id of the Account to use. If 1272 "null", the primary account for the capability that defines the 1273 data type is used. 1275 o *ifInState*: "String|null" This is a state string as returned by 1276 the _Foo/get_ method. If supplied, the string must match the 1277 current state, otherwise the method will be aborted and a 1278 "stateMismatch" error returned. If "null", any changes will be 1279 applied to the current state. 1281 o *create*: "String[Foo]|null" A map of _creation id_ (an arbitrary 1282 string set by the client) to Foo objects, or "null" if no objects 1283 are to be created. The Foo object type definition MAY define 1284 default values for properties. Any such property MAY be omitted 1285 by the client. The client MUST omit any properties that may only 1286 be set by the server (for example, the _id_ property on most 1287 object types). 1289 o *update*: "String[PatchObject]|null" A map of id to a Patch object 1290 to apply to the current Foo object with that id, or "null" if no 1291 objects are to be updated. A _PatchObject_ is of type 1292 "String[*]", and represents an unordered set of patches. The keys 1293 are a path in [RFC6901] JSON pointer format, with an implicit 1294 leading "/" (i.e. prefix each key with "/" before applying the 1295 JSON pointer evaluation algorithm). All paths MUST also conform 1296 to the following restrictions; if there is any violation, the 1297 update MUST be rejected with an "invalidPatch" error: 1299 * The pointer MUST NOT reference inside an array (i.e. you MUST 1300 NOT insert/delete from an array; the array MUST be replaced in 1301 its entirety instead). 1303 * All parts prior to the last (i.e. the value after the final 1304 slash) MUST already exist on the object being patched. 1306 * There MUST NOT be two patches in the PatchObject where the 1307 pointer of one is the prefix of the pointer of the other, e.g. 1308 "alerts/1/offset" and "alerts". 1310 The value associated with each pointer determines how to apply 1311 that patch: 1313 * If "null", set to the default value if specified for this 1314 property, otherwise remove the property from the patched 1315 object. If the key is not present in the parent, this a no-op. 1317 * Anything else: The value to set for this property (this may be 1318 a replacement or addition to the object being patched). 1320 Any server-set properties MAY be included in the patch if their 1321 value is identical to the current server value (before applying 1322 the patches to the object). Otherwise, the update MUST be 1323 rejected with an _invalidProperties_ SetError. This patch 1324 definition is designed such that an entire Foo object is also a 1325 valid PatchObject. The client MAY choose to optimise network 1326 usage by just sending the diff, or MAY just send the whole object; 1327 the server processes it the same either way. 1329 o *destroy*: "String[]|null" A list of ids for Foo objects to 1330 permanently delete, or "null" if no objects are to be destroyed. 1332 Each creation, modification or destruction of an object is considered 1333 an atomic unit. It is permissible for the server to commit changes 1334 to some objects but not others, however it is not permissible to only 1335 commit part of an update to a single record (e.g. update a _name_ 1336 property but not a _count_ property, if both are supplied in the 1337 update object). 1339 The final state MUST be valid after the Foo/set is finished, however 1340 the server may have to transition through invalid intermediate states 1341 (not exposed to the client) while processing the individual 1342 create/update/destroy requests. For example, suppose there is a 1343 "name" property that must be unique. A single method call could 1344 rename an object A => B, and simultaneously rename another object B 1345 => A. If the final state is valid, this is allowed. Otherwise, each 1346 creation, modification or destruction of an object should be 1347 processed sequentially and accepted/rejected based on the current 1348 server state. 1350 If a create, update or destroy is rejected, the appropriate error 1351 MUST be added to the notCreated/notUpdated/notDestroyed property of 1352 the response and the server MUST continue to the next create/update/ 1353 destroy. It does not terminate the method. 1355 If an id given cannot be found, the update or destroy MUST be 1356 rejected with a "notFound" set error. 1358 The server MAY skip an update (rejecting it with a "willDestroy" 1359 SetError) if that object is destroyed in the same /set request. 1361 Some record objects may hold references to others (foreign keys). 1362 When records are created or modified, they may reference other 1363 records being created _in the same API request_ by using the creation 1364 id prefixed with a "#". The order of the method calls in the request 1365 by the client MUST be such that the record being referenced is 1366 created in the same or an earlier call. The server thus never has to 1367 look ahead. Instead, while processing a request (a series of method 1368 calls), the server MUST keep a simple map for the duration of the 1369 request of creation id to record id for each newly created record, so 1370 it can substitute in the correct value if necessary in later method 1371 calls. 1373 Creation ids are not scoped by type but are a single map for all 1374 types. A client SHOULD NOT reuse a creation id anywhere in the same 1375 API request. If a creation id is reused, the server MUST map the 1376 creation id to the most recently created item with that id. To allow 1377 easy proxying of API requests, an initial set of creation id to real 1378 id values may be passed with a request (see The Request object 1379 specification above). 1381 The response has the following arguments: 1383 o *accountId*: "String" The id of the account used for the call. 1385 o *oldState*: "String|null" The state string that would have been 1386 returned by _Foo/get_ before making the requested changes, or 1387 "null" if the server doesn't know what the previous state string 1388 was. 1390 o *newState*: "String" The state string that will now be returned by 1391 _Foo/get_. 1393 o *created*: "String[Foo]|null" A map of the creation id to an 1394 object containing any properties of the created Foo object that 1395 were not sent by the client. This includes all server-set 1396 properties (such as the _id_ in most object types) and any 1397 properties that were omitted by the client and so set to a default 1398 by the server. This argument is "null" if no Foo objects were 1399 successfully created. 1401 o *updated*: "String[Foo|null]|null" The _keys_ in this map are the 1402 ids of all Foos that were successfully updated, or "null" if none 1403 successful. The _value_ for each id is a Foo object containing 1404 any property that changed in a way _not_ explicitly requested by 1405 the _PatchObject_ sent to the server, or "null" if none. This 1406 lets the client know of any changes to server-set or computed 1407 properties. 1409 o *destroyed*: "String[]|null" A list of Foo ids for records that 1410 were successfully destroyed, or "null" if none successful. 1412 o *notCreated*: "String[SetError]|null" A map of creation id to a 1413 SetError object for each record that failed to be created, or 1414 "null" if all successful. 1416 o *notUpdated*: "String[SetError]|null" A map of Foo id to a 1417 SetError object for each record that failed to be updated, or 1418 "null" if all successful. 1420 o *notDestroyed*: "String[SetError]|null" A map of Foo id to a 1421 SetError object for each record that failed to be destroyed, or 1422 "null" if all successful. 1424 A *SetError* object has the following properties: 1426 o *type*: "String" The type of error. 1428 o *description*: "String|null" A description of the error to help 1429 debug with an explanation of what the problem was. This is a non- 1430 localised string, and is not intended to be shown directly to end 1431 users. 1433 The following SetError types are defined and may be returned for set 1434 operations on any record type where appropriate: 1436 o "forbidden": (create; update; destroy) The create/update/destroy 1437 would violate an ACL or other permissions policy. 1439 o "overQuota": (create) The create would exceed a server-defined 1440 limit on the number or total size of objects of this type. 1442 o "tooLarge": (create; update) The create/update would result in an 1443 object that exceeds a server-defined limit for the maximum size of 1444 a single object of this type. 1446 o "rateLimit": (create) Too many objects of this type have been 1447 created recently, and a server-defined rate limit has been 1448 reached. It may work if tried again later. 1450 o "notFound": (update; destroy) The id given cannot be found. 1452 o "invalidPatch": (update) The PatchObject given to update the 1453 record was not a valid patch (see the patch description). 1455 o "willDestroy" (update) The client requested an object be both 1456 updated and destroyed in the same /set request, and the server has 1457 decided to therefore ignore the update. 1459 o "invalidProperties": (create; update) The record given is invalid 1460 in some way. For example: 1462 * It contains properties which are invalid according to the type 1463 specification of this record type. 1465 * It contains a property that may only be set by the server (e.g. 1466 "id") and are different to the current value. Note, to allow 1467 clients to pass whole objects back, it is not an error to 1468 include a server-set property so long as the value is identical 1469 to the current value on the server (or the value that will be 1470 set by the server if a create). 1472 * There is a reference to another record (foreign key) and the 1473 given id does not correspond to a valid record. 1475 The SetError object SHOULD also have a property called 1476 _properties_ of type "String[]" that lists *all* the properties 1477 that were invalid. Individual methods MAY specify more specific 1478 errors for certain conditions that would otherwise result in an 1479 invalidProperties error. If the condition of one of these is met, 1480 it MUST be returned instead of the invalidProperties error. 1482 o "singleton": (create; destroy) This is a singleton type, so you 1483 cannot create another one or destroy the existing one. 1485 Other possible SetError types MAY be given in specific method 1486 descriptions. Other properties MAY also be present on the _SetError_ 1487 object, as described in the relevant methods. 1489 The following additional errors may be returned instead of the _Foo/ 1490 set_ response: 1492 "requestTooLarge": The total number of objects to create, update or 1493 destroy exceeds the maximum number the server is willing to process 1494 in a single method call. 1496 "stateMismatch": An "ifInState" argument was supplied and it does not 1497 match the current state. 1499 5.4. /copy 1501 The only way to move Foo records *between* two different accounts is 1502 to copy them using the _Foo/copy_ method, then once the copy has 1503 succeeded, delete the original. The _onSuccessDestroyOriginal_ 1504 argument allows you to try to do this in one method call, however 1505 note that the two different actions are not atomic, and so it is 1506 possible for the copy to succeed but the original not to be destroyed 1507 for some reason. 1509 The _Foo/copy_ method takes the following arguments: 1511 o *fromAccountId*: "String|null" The id of the account to copy 1512 records from. If "null", the primary account for the capability 1513 that defines the data type is used. 1515 o *toAccountId*: "String|null" The id of the account to copy records 1516 to. If "null", the primary account for the capability that 1517 defines the data type is used. 1519 o *create*: "String[Foo]" A map of _creation id_ to a Foo object. 1520 The object MUST contain an id property: the id of the record to be 1521 copied. Any other properties included are used instead of the 1522 current value for that property on the original when creating the 1523 copy. 1525 o *onSuccessDestroyOriginal*: "Boolean" (default: false) If "true", 1526 an attempt will be made to destroy the original records that were 1527 successfully copied: after emitting the _Foo/copy_ response, but 1528 before processing the next method, the server MUST make a single 1529 call to _Foo/set_ to destroy the original of each successfully 1530 copied record; the output of this is added to the responses as 1531 normal to be returned to the client. 1533 Each record copy is considered an atomic unit which may succeed or 1534 fail individually. Copying successfully MUST create a new record 1535 object, with separate ids and mutable properties to the original, 1536 even if copied within a single account. 1538 The response has the following arguments: 1540 o *fromAccountId*: "String" The id of the account records were 1541 copied from. 1543 o *toAccountId*: "String" The id of the account records were copied 1544 to. 1546 o *created*: "String[Foo]|null" A map of the creation id to an 1547 object containing any properties of the copied Foo object that are 1548 set by the server (such as the _id_ in most object types). This 1549 argument is "null" if no Foo objects were successfully copied. 1551 o *notCreated*: "String[SetError]|null" A map of creation id to a 1552 SetError object for each record that failed to be copied, "null" 1553 if none. 1555 The *SetError* may be any of the standard set errors that may be 1556 returned for a _create_ or _update_. In addition, the following 1557 SetError is defined: 1559 "alreadyExists": The server forbids duplicates and the record already 1560 exists in the target account. An _existingId_ property of type 1561 "String" MUST be included on the error object with the id of the 1562 existing record. 1564 The following additional errors may be returned instead of the _Foo/ 1565 copy_ response: 1567 "fromAccountNotFound": A _fromAccountId_ was explicitly included with 1568 the request, but it does not correspond to a valid account; or, 1569 _fromAccountId_ was null but there is no primary account for the 1570 capability that defines the data type. 1572 "toAccountNotFound": A _toAccountId_ was explicitly included with the 1573 request, but it does not correspond to a valid account; or, 1574 _toAccountId_ was null but there is no primary account for the 1575 capability that defines the data type. 1577 "fromAccountNotSupportedByMethod": The _fromAccountId_ given 1578 corresponds to a valid account, but the account does not support this 1579 data type. 1581 "toAccountNotSupportedByMethod": The _toAccountId_ given corresponds 1582 to a valid account, but the account does not support this data type. 1584 5.5. /query 1586 For data sets where the total amount of data is expected to be very 1587 small, clients can just fetch the complete set of data and then do 1588 any sorting/filtering locally. However, for large data sets (e.g. 1589 multi-gigabyte mailboxes), the client needs to be able to 1590 search/sort/window the data type on the server. 1592 A query on the set of Foos in an account is made by calling _Foo/ 1593 query_. This takes a number of arguments to determine which records 1594 to include, how they should be sorted, and which part of the result 1595 should be returned (the full list may be _very_ long). The result is 1596 returned as a list of Foo ids. 1598 A call to _Foo/query_ takes the following arguments: 1600 o *accountId*: "String|null" The id of the Account to use. If 1601 "null", the primary account for the capability that defines the 1602 data type is used. 1604 o *filter*: "FilterOperator|FilterCondition|null" Determines the set 1605 of Foos returned in the results. If "null", all objects in the 1606 account of this type are included in the results. A 1607 *FilterOperator* object has the following properties: 1609 * *operator*: "String" This MUST be one of the following strings: 1610 "AND"/"OR"/"NOT": 1612 + *AND*: all of the conditions must match for the filter to 1613 match. 1615 + *OR*: at least one of the conditions must match for the 1616 filter to match. 1618 + *NOT*: none of the conditions must match for the filter to 1619 match. 1621 * *conditions*: "(FilterOperator|FilterCondition)[]" The 1622 conditions to evaluate against each record. 1624 A *FilterCondition* is an "object", whose allowed properties and 1625 semantics depend on the data type and is defined in the _/query_ 1626 method specification for that type. It MUST NOT have an 1627 _operator_ property. 1629 o *sort*: "Comparator[]|null" Lists the names of properties to 1630 compare between two Foo records, and how to compare them, to 1631 determine which comes first in the sort. If two Foo records have 1632 an identical value for the first comparator, the next comparator 1633 will be considered and so on. If all comparators are the same 1634 (this includes the case where an empty array or "null" is given as 1635 the _sort_ argument), the sort order is server-dependent, but MUST 1636 be stable between calls to Foo/query. A *Comparator* has the 1637 following properties: 1639 * *property*: "String" The name of the property on the Foo 1640 objects to compare. 1642 * *isAscending*: "Boolean" (optional; default: true) If true, 1643 sort in ascending order. If false, reverse the comparator's 1644 results to sort in descending order. 1646 * *collation*: "String" (optional; default is server-dependent) 1647 The identifier, as registered in the collation registry defined 1648 in [RFC4790], for the algorithm to use when comparing the order 1649 of strings. The algorithms the server supports are advertised 1650 in the capabilities object returned with the JMAP Session 1651 object. If omitted, the default algorithm is server-dependent, 1652 but: 1654 1. It MUST be unicode-aware. 1656 2. It SHOULD have reasonable default behavior for many 1657 languages when the user's language is unknown. 1659 3. It MAY be selected based on out-of-band information about 1660 the user's language/locale. 1662 4. It SHOULD be case-insensitive where such a concept makes 1663 sense for a language/locale. 1665 The "i;unicode-casemap" collation ([RFC5051]) and the Unicode 1666 Collation Algorithm () 1667 are two examples that fulfil these criterion. When the 1668 property being compared is not a string, the _collation_ 1669 property is ignored and the following comparison rules apply 1670 based on the type. In ascending order: 1672 + "Boolean": "false" comes before "true". 1674 + "Number": A lower number comes before a higher number. 1676 + "Date"/"UTCDate": The earlier date comes first. 1678 The object may also have additional properties as required for 1679 specific sort operations defined in a type's /query method. 1681 o *position*: "Int" (default: 0) The 0-based index of the first id 1682 in the full list of results to return. If a negative value is 1683 given, it is an offset from the end of the list. Specifically, 1684 the negative value MUST be added to the total number of results 1685 given the filter, and if still negative clamped to "0". This is 1686 now the 0-based index of the first id to return. If the index is 1687 greater than or equal to the total number of objects in the 1688 results list then the _ids_ array in the response will be empty, 1689 but this is not an error. 1691 o *anchor*: "String|null" A Foo id. If supplied the _position_ 1692 argument is ignored. The index of this id in the results will be 1693 used in combination with the "anchorOffset" argument to determine 1694 the index of the first result to return (see below for more 1695 details). 1697 o *anchorOffset*: "Int|null" The index of the first result to return 1698 relative to the index of the anchor. This MAY be negative. For 1699 example, "-1" means the first Foo before the anchor Foo should be 1700 the first result in the results returned (see below for more 1701 details). 1703 o *limit*: "PositiveInt|null" The maximum number of results to 1704 return. If "null", no limit presumed. The server MAY choose to 1705 enforce a maximum "limit" argument. In this case, if a greater 1706 value is given (or if it is "null"), the limit should be clamped 1707 to the maximum; since the total number of results in the query is 1708 returned, the client can determine if it has received all the 1709 results. If a negative value is given, the call MUST be rejected 1710 with an "invalidArguments" error. 1712 o *calculateTotal*: "Boolean" (default: false) Does the client wish 1713 to know the total number of results in the query? This may be 1714 slow and expensive for servers to calculate, particularly with 1715 complex filters, so clients should take care to only request the 1716 total when needed. 1718 If an *anchor* argument is given, then after filtering and sorting 1719 the anchor is looked for in the results. If found, the *anchor 1720 offset* is then added to its index. If the resulting index is now 1721 negative, it is clamped to 0. This index is now used exactly as 1722 though it were supplied as the "position" argument. If the anchor is 1723 not found, the call is rejected with an "anchorNotFound" error. 1725 If an _anchor_ is specified, any position argument supplied by the 1726 client MUST be ignored. If _anchorOffset_ is "null", it defaults to 1727 "0". If no _anchor_ is supplied, any anchor offset argument MUST be 1728 ignored. 1730 A client can use _anchor_ instead of _position_ to find the index of 1731 an id within a large set of results. 1733 The response has the following arguments: 1735 o *accountId*: "String" The id of the account used for the call. 1737 o *filter*: "FilterOperator|FilterCondition|null" The filter used. 1738 Echoed back from the call. 1740 o *sort*: "Comparator[]|null" The sort options used. Echoed back 1741 from the call. 1743 o *queryState*: "String" A string encoding the current state of the 1744 query on the server. This string MUST change if the results of 1745 the query (i.e. the matching ids and their sort order) have 1746 changed. The queryState string MAY change if something has 1747 changed on the server which means the results may have changed but 1748 the server doesn't know for sure. The queryState string only 1749 represents the ordered list of ids that match the particular query 1750 (including its sort/filter). There is no requirement for it to 1751 change if a property on an object matching the query changes but 1752 the query results are unaffected (indeed, it is more efficient if 1753 the queryState string does not change in this case). The 1754 queryState string only has meaning when compared to future 1755 responses to a query with the same type/sort/filter, or when used 1756 with /queryChanges to fetch changes. Should a client receive back 1757 a response with a different queryState string to a previous call, 1758 it MUST either throw away the currently cached query and fetch it 1759 again (note, this does not require fetching the records again, 1760 just the list of ids) or, call _Foo/queryChanges_ to get the 1761 difference. 1763 o *canCalculateChanges*: "Boolean" This is "true" if the server 1764 supports calling _Foo/queryChanges_ with these "filter"/"sort" 1765 parameters. Note, this does not guarantee that the _Foo/ 1766 queryChanges_ call will succeed, as it may only be possible for a 1767 limited time afterwards due to server internal implementation 1768 details. 1770 o *position*: "PositiveInt" The 0-based index of the first result in 1771 the "ids" array within the complete list of query results. 1773 o *total*: "PositiveInt" (only if requested) The total number of 1774 foos in the results (given the _filter_). This argument MUST be 1775 omitted if the _calculateTotal_ request argument is not "true". 1777 o *ids*: "String[]" The list of ids for each foo in the query 1778 results, starting at the index given by the _position_ argument of 1779 this response, and continuing until it hits the end of the results 1780 or reaches the "limit" number of ids. If _position_ is >= 1781 _total_, this MUST be the empty list. 1783 The following additional errors may be returned instead of the _Foo/ 1784 query_ response: 1786 "anchorNotFound": An anchor argument was supplied, but it cannot be 1787 found in the results of the query. 1789 "unsupportedSort": The _sort_ is syntactically valid, but includes a 1790 property the server does not support sorting on, or a collation 1791 method it does not recognise. 1793 "unsupportedFilter": The _filter_ is syntactically valid, but the 1794 server cannot process it. 1796 5.6. /queryChanges 1798 The "Foo/queryChanges" call allows a client to efficiently update the 1799 state of any cached foo query to match the new state on the server. 1800 It takes the following arguments: 1802 o *accountId*: "String|null" The id of the account to use. If 1803 "null", the primary account for the capability that defines the 1804 data type will be used. 1806 o *filter*: "FilterOperator|FilterCondition|null" The filter 1807 argument that was used with _Foo/query_. 1809 o *sort*: "Comparator[]|null" The sort argument that was used with 1810 _Foo/query_. 1812 o *sinceQueryState*: "String" The current state of the query in the 1813 client. This is the string that was returned as the _queryState_ 1814 argument in the _Foo/query_ response with the same sort/filter. 1815 The server will return the changes made to the query since this 1816 state. 1818 o *maxChanges*: "PositiveInt|null" The maximum number of changes to 1819 return in the response. See error descriptions below for more 1820 details. 1822 o *upToId*: "String|null" The last (highest-index) id the client 1823 currently has cached from the query results. When there are a 1824 large number of results, in a common case the client may have only 1825 downloaded and cached a small subset from the beginning of the 1826 results. If the sort and filter are both only on immutable 1827 properties, this allows the server to omit changes after this 1828 point in the results, which can significantly increase efficiency. 1829 If they are not immutable, this argument is ignored. 1831 o *calculateTotal*: "Boolean" (default: false) Does the client wish 1832 to know the total number of results now in the query? This may be 1833 slow and expensive for servers to calculate, particularly with 1834 complex filters, so clients should take care to only request the 1835 total when needed. 1837 The response has the following arguments: 1839 o *accountId*: "String" The id of the account used for the call. 1841 o *filter*: "FilterOperator|FilterCondition|null" The filter used. 1842 Echoed back from the call. 1844 o *sort*: "Comparator[]|null" The sort options used. Echoed back 1845 from the call. 1847 o *oldQueryState*: "String" This is the "sinceQueryState" argument 1848 echoed back; the state from which the server is returning changes. 1850 o *newQueryState*: "String" This is the state the query will be in 1851 after applying the set of changes to the old state. 1853 o *upToId*: "String|null" Echoed back from the call. 1855 o *total*: "PositiveInt" (only if requested) The total number of 1856 foos in the results (given the _filter_). This argument MUST be 1857 omitted if the _calculateTotal_ request argument is not "true". 1859 o *removed*: "String[]" The _id_ for every foo that was in the query 1860 results in the old state and is not in the results in the new 1861 state. If the sort and filter are both only on immutable 1862 properties and an _upToId_ is supplied and exists in the results, 1863 any ids that were removed but have a higher index than _upToId_ 1864 SHOULD be omitted. If the server cannot calculate this exactly, 1865 the server MAY return extra foos in addition that may have been in 1866 the old results but are not in the new results. If the _filter_ 1867 or _sort_ includes a mutable property, the server MUST include all 1868 foos in the current results for which this property MAY have 1869 changed. 1871 o *added*: "AddedItem[]" The id and index in the query results (in 1872 the new state) for every foo that has been added to the results 1873 since the old state AND every foo in the current results that was 1874 included in the _removed_ array (due to a filter or sort based 1875 upon a mutable property). If the sort and filter are both only on 1876 immutable properties and an _upToId_ is supplied and exists in the 1877 results, any ids that were added but have a higher index than 1878 _upToId_ SHOULD be omitted. The array MUST be sorted in order of 1879 index, lowest index first. An *AddedItem* object has the 1880 following properties: 1882 * *id*: "String" 1884 * *index*: "PositiveInt" 1886 The result of this is that if the client has a cached sparse array of 1887 foo ids in the results in the old state: 1889 fooIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ] 1891 then if it *splices out* all foos in the removed array: 1893 removed = [ "id2", ... ]; 1894 fooIds => [ "id1", null, null, "id3", "id4", null, null, null ] 1896 and *splices in* (in order) all of the foos in the added array: 1898 added = [{ id: "id5", index: 0, ... }]; 1899 fooIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ] 1901 and *truncates* or *extends* to the new total length, then the 1902 results will now be in the new state. 1904 The following additional errors may be returned instead of the _Foo/ 1905 queryChanges_ response: 1907 "tooManyChanges": There are more changes than the client's 1908 _maxChanges_ argument. Each item in the removed or added array is 1909 considered as one change. The client may retry with a higher max 1910 changes or invalidate its cache of the query results. 1912 "cannotCalculateChanges": The server cannot calculate the changes 1913 from the queryState string given by the client. Usually due to the 1914 client's state being too old. The client MUST invalidate its cache 1915 of the query results. 1917 5.7. Examples 1919 Suppose we have a type _Todo_ with the following properties: 1921 o *id*: "String" (immutable; server-set) The id of the object. 1923 o *title*: "String" A brief summary of what is to be done. 1925 o *keywords*: "String[Boolean]" (default: ) A set of keywords that 1926 apply to the todo. The set is represented as an object, with the 1927 keys being the _keywords_. The value for each key in the object 1928 MUST be "true". 1930 o *neuralNetworkTimeEstimation*: "Number" (server-set) The title and 1931 keywords are fed into the server's state-of-the-art neural network 1932 to get an estimation of how long this todo will take, in seconds. 1934 and the server supports all the standard methods for the type, 1935 including querying by keyword using the syntax "{ hasKeyword: "foo" 1936 }" in the _filter_ argument to _/query_. 1938 Now, a client might want to display the list of todos with either a 1939 "music" keyword or a "video" keyword, so it makes the following 1940 method call: 1942 [[ "Todo/query", { 1943 "filter": { 1944 "operator": "OR", 1945 "conditions": [ 1946 { "hasKeyword": "music" }, 1947 { "hasKeyword": "video" } 1948 ] 1949 }, 1950 "sort": [{ "property": "title" }], 1951 "position": 0, 1952 "limit": 10 1953 }, "0" ], 1954 [ "Todo/get", { 1955 "#ids": { 1956 "resultOf": "0", 1957 "name": "Todo/query", 1958 "path": "/ids" 1959 }, 1960 }, "1" ]] 1962 This would query the server for the set of todos with a keyword of 1963 "music", sorted by title, and limited to the first 10 results. It 1964 fetches the full object for each of these Todos using backreferences 1965 to reference the result of the query. The response might look 1966 something like: 1968 [[ "Todo/query", { 1969 "accountId": "x", 1970 "filter": { 1971 "operator": "OR", 1972 "conditions": [ 1973 { "hasKeyword": "music" }, 1974 { "hasKeyword": "video" } 1975 ] 1976 }, 1977 "sort": [{ "property": "title" }], 1978 "queryState": "y13213", 1979 "canCalculateChanges": true, 1980 "position": 0, 1981 "ids": [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ] 1982 }, "0" ], 1983 [ "Todo/get", { 1984 "accountId": "x", 1985 "state": "10324", 1986 "list": [{ 1987 "id": "a", 1988 "title": "Practise Piano", 1989 "keywords": { 1990 "music": true, 1991 "beethoven": true, 1992 "mozart": true, 1993 "liszt": true, 1994 "rachmaninov": true 1995 }, 1996 "neuralNetworkTimeEstimation": 3600 1997 }, { 1998 "id": "b", 1999 "title": "Listen to Daft Punk", 2000 "keywords": { 2001 "music": true, 2002 "trance": true 2003 }, 2004 "neuralNetworkTimeEstimation": 18000 2005 }, 2006 ... 2007 ] 2008 }, "1" ]] 2010 Now suppose the user adds a keyword "chopin" and removes the keyword 2011 "mozart" from the "Practise Piano" task. The client may send the 2012 whole object to the server, as this is a valid PatchObject: 2014 [[ "Todo/set", { 2015 "ifInState": "10324", 2016 "update": { 2017 "a": { 2018 "id": "a", 2019 "title": "Practise Piano", 2020 "keywords": { 2021 "music": true, 2022 "beethoven": true, 2023 "chopin": true, 2024 "liszt": true, 2025 "rachmaninov": true, 2026 } 2027 "neuralNetworkTimeEstimation": 360 2028 } 2029 } 2030 }, "0" ]] 2032 or it may send a minimal patch: 2034 [[ "Todo/set", { 2035 "ifInState": "10324", 2036 "update": { 2037 "a": { 2038 "keywords/chopin": true, 2039 "keywords/mozart": null 2040 } 2041 } 2042 }, "0" ]] 2044 The effect is exactly the same on the server in either case, and 2045 presuming the server is still in state "10324" it will probably 2046 return success: 2048 [[ "Todo/set", { 2049 "accountId": "x", 2050 "oldState": "10324", 2051 "newState": "10329", 2052 "updated": { 2053 "a": { 2054 "neuralNetworkTimeEstimation": 5400 2055 } 2056 } 2057 }, "0" ]] 2059 The server changed the "neuralNetworkTimeEstimation" property on the 2060 object as part of this change; as this changed in a way _not_ 2061 explicitly requested by the PatchObject sent to the server, it is 2062 returned with the "updated" confirmation. 2064 Now, suppose another user deleted the "Listen to Daft Punk" todo. 2065 The first user will receive a push notification (see later in the 2066 spec) with the changed state string for the "Todo" type. Since the 2067 new string does not match its current state, it knows it needs to 2068 check for updates. It may make a request like: 2070 [[ "Todo/changes", { 2071 "accountId": "x", 2072 "sinceState": "10324", 2073 "maxChanges": 50, 2074 }, "0" ], 2075 [ "Todo/queryChanges", { 2076 "filter": { 2077 "operator": "OR", 2078 "conditions": [ 2079 { "hasKeyword": "music" }, 2080 { "hasKeyword": "video" } 2081 ] 2082 }, 2083 "sort": [{ "property": "title" }], 2084 "sinceQueryState": "y13213" 2085 "maxChanges": 50, 2086 }, "1" ]] 2088 and receive in response: 2090 [[ "Todo/changes", { 2091 "accountId": "x", 2092 "oldState": "10324", 2093 "newState": "871903", 2094 "hasMoreChanges": false, 2095 "created": [], 2096 "updated": [], 2097 "destroyed": ["b"] 2098 }, "0" ], 2099 [ "Todo/queryChanges", { 2100 "filter": { 2101 "operator": "OR", 2102 "conditions": [ 2103 { "hasKeyword": "music" }, 2104 { "hasKeyword": "video" } 2105 ] 2106 }, 2107 "sort": [{ "property": "title" }], 2108 "oldQueryState": "y13213" 2109 "newQueryState": "y13218" 2110 "removed": ["b"], 2111 "added": null 2112 }, "1" ]] 2114 Suppose the user has access to another account "y", for example a 2115 team account shared between multiple users. To move an existing Todo 2116 from account "x", the client would call: 2118 [[ "Todo/copy", { 2119 "fromAccountId": "x", 2120 "toAccountId": "y", 2121 "create": { 2122 "k5122": { 2123 "id": "a" 2124 } 2125 }, 2126 "onSuccessDestroyOriginal": true, 2127 }, "0" ]] 2129 The server successfully copies the Todo to a new account (where it 2130 receives a new id) and deletes the original. Due to the implicit 2131 call to "Todo/set", there are two responses to the single method 2132 call, both with the same client id: 2134 [[ "Todo/copy", { 2135 "fromAccountId": "x", 2136 "toAccountId": "y", 2137 "created": { 2138 "k5122": { 2139 "id": "97" 2140 } 2141 }, 2142 "notCreated": null 2143 }, "0" ], 2144 [ "Todo/set", { 2145 "accountId": "x", 2146 "oldState": "871903" 2147 "newState": "871909", 2148 "destroyed": [ "a" ], 2149 ... 2150 }, "0" ]] 2152 6. Binary data 2154 Binary data is referenced by a _blobId_ in JMAP, and uploaded/ 2155 downloaded separately to the core API. The blobId solely represents 2156 the raw bytes of data, not any associated metadata such as a file 2157 name or content type. Such metadata is stored alongside the blobId 2158 in the object referencing it. The data represented by a blobId is 2159 immutable. 2161 Any blobId that exists within an account may be used when creating/ 2162 updating another object in that account. For example, an Email type 2163 may have a blobId that represents the [RFC5322] representation of the 2164 message. A client could create a new Email object with an attachment 2165 and use this blobId, in effect attaching the old message to the new 2166 one. Similarly it could attach any existing attachment of an old 2167 message without having to download and upload it again. 2169 When the client uses a blobId in a create/update, the server MAY 2170 assign a new blobId to refer to the same binary data within the new/ 2171 updated object. If it does so, it MUST return any properties that 2172 contain a changed blobId in the created/updated response so the 2173 client gets the new ids. 2175 A blob that is not referenced by a JMAP object (e.g. as a message 2176 attachment) MAY be deleted by the server to free up resources. 2177 Uploads (see below) are initially unreferenced blobs. To ensure 2178 interoperability: 2180 o The server SHOULD use a separate quota for unreferenced blobs to 2181 the user's usual quota. 2183 o This quota SHOULD be at least the maximum total size that a single 2184 object can reference on this server. For example, if supporting 2185 JMAP Mail, this should be at least the maximum total attachments 2186 size for a message. 2188 o When an upload would take the user over quota, the server MUST 2189 delete unreferenced blobs in date order, oldest first, until there 2190 is room for the new blob. 2192 o Except where quota restrictions force early deletion, an 2193 unreferenced blob MUST NOT be deleted for at least 1 hour from the 2194 time of upload; if reuploaded, the same blobId MAY be returned, 2195 but this SHOULD reset the expiry time. 2197 o A blob MUST NOT be deleted during the method call which removed 2198 the last reference, so that a client can issue a create and a 2199 destroy that both reference the blob within the same method call. 2201 6.1. Uploading binary data 2203 There is a single endpoint which handles all file uploads for an 2204 account, regardless of what they are to be used for. The JMAP 2205 Session object has an _uploadUrl_ property in [RFC6570] URI Template 2206 (level 1) format, which MUST contain a variable called "accountId". 2207 The client may use this template in combination with an _accountId_ 2208 to get the URL of the file upload resource. 2210 To upload a file, the client submits an authenticated POST request to 2211 the file upload resource. 2213 A successful request MUST return a single JSON object with the 2214 following properties as the response: 2216 o *accountId*: "String" The id of the account used for the call. 2218 o *blobId*: "String", The id representing the binary data uploaded. 2219 The data for this id is immutable. The id _only_ refers to the 2220 binary data, not any metadata. 2222 o *type*: "String" The media type of the file (as specified in 2223 [RFC6838], section 4.2) as set in the Content-Type header of the 2224 upload HTTP request. 2226 o *size*: "PositiveInt" The size of the file in octets. 2228 If identical binary content to an existing blob in the account is 2229 uploaded, the existing blobId MAY be returned. 2231 6.2. Downloading binary data 2233 The JMAP Session object has a _downloadUrl_ property, which is in 2234 [RFC6570] URI Template (level 1) format. The URL MUST contain 2235 variables called "accountId", "blobId", "type" and "name". 2237 To download a file, the client makes an authenticated GET request to 2238 the download URL with the appropriate variables substituted in: 2240 o "accountId": The id of the account to which the record with the 2241 blobId belongs. 2243 o "blobId": The blobId representing the data of the file to 2244 download. 2246 o "type": The type for the server to set in the "Content-Type" 2247 header of the response; the blobId only represents the binary data 2248 and does not have a content-type inately associated with it. 2250 o "name": The name for the file; the server MUST return this as the 2251 filename if it sets a "Content-Disposition" header. 2253 6.3. Blob/copy 2255 Binary data may be copied *between* two different accounts using the 2256 _Blob/copy_ method, rather than having to download then reupload on 2257 the client. 2259 The _Blob/copy_ method takes the following arguments: 2261 o *fromAccountId*: "String" The id of the account to copy blobs 2262 from. 2264 o *toAccountId*: "String" The id of the account to copy blobs to. 2266 o *blobIds*: "String[]" A list of ids of blobs to copy to the other 2267 account. 2269 The response has the following arguments: 2271 o *fromAccountId*: "String" The id of the account emails were copied 2272 from. 2274 o *toAccountId*: "String" The id of the account emails were copied 2275 to. 2277 o *copied*: "String[String]|null" A map of the blobId in the 2278 _fromAccount_ to the id for the blob in the _toAccount_, or "null" 2279 if none were successfully copied. 2281 o *notCopied*: "String[SetError]|null" A map of blobId to a SetError 2282 object for each blob that failed to be copied, "null" if none. 2284 The *SetError* may be any of the standard set errors that may be 2285 returned for a _create_. In addition, the "notFound" SetError error 2286 may be returned if the blobId to be copied cannot be found. 2288 The following additional errors may be returned instead of the _Blob/ 2289 copy_ response: 2291 "fromAccountNotFound": A _fromAccountId_ was explicitly included with 2292 the request, but it does not correspond to a valid account. 2294 "toAccountNotFound": A _toAccountId_ was explicitly included with the 2295 request, but it does not correspond to a valid account. 2297 7. Push 2299 Push notifications allow clients to efficiently update (almost) 2300 instantly to stay in sync with data changes on the server. In JMAP, 2301 push notifications occur out-of-band (i.e. not over the same 2302 connection as API exchanges), so that they can make use of efficient 2303 native push mechanisms on different platforms. 2305 The general model for push is simple and sends minimal data over the 2306 push channel. The format allows multiple changes to be coalesced 2307 into a single push update, and the frequency of pushes to be rate 2308 limited by the server. It doesn't matter if some push events are 2309 dropped before they reach the client; it will still get all changes 2310 next time it syncs. 2312 7.1. The StateChange object 2314 When something changes on the server, the server pushes a 2315 *StateChange* object to the client. A *StateChange* object has the 2316 following properties: 2318 o *changed*: "String[TypeState]" A map of _account id_ to an object 2319 encoding the state of data types that have changed for that 2320 account since the last push event, for each of the accounts to 2321 which the user has access and for which something has changed. A 2322 *TypeState* object is a map. The keys are the type name "Foo" 2323 (e.g. "Mailbox" or "Email"), and the value is the _state_ 2324 property that would currently be returned by a call to _Foo/get_. 2326 The client can compare the new state strings with its current 2327 values to see whether it has the current data for these types. If 2328 not, the changes can then be efficiently fetched in a single 2329 standard API request (using the _/changes_ type methods). 2331 7.1.1. Example 2333 In this example, the server has almalgamated a few changes together 2334 across two different accounts the user has access to, before pushing 2335 the following StateChange object to the client: 2337 { 2338 "changed": { 2339 "a3123": { 2340 "Email": "d35ecb040aab", 2341 "EmailDelivery": "428d565f2440", 2342 "CalendarEvent": "87accfac587a" 2343 }, 2344 "a43461d": { 2345 "Mailbox": "0af7a512ce70", 2346 "CalendarEvent": "7a4297cecd76" 2347 }, 2348 } 2349 } 2351 The client can compare the state strings with its current state for 2352 the Email, CalendarEvent etc. object types in the appropriate 2353 accounts to see if it needs to fetch changes. If the client is 2354 itself making changes, it may receive a StateChange object while the 2355 /set API call is in flight. It can wait until the call completes and 2356 then compare if the new state string after the /set is the same as 2357 was pushed in the StateChange object; if so, it does not need to 2358 waste a request asking for changes it already knows. 2360 7.2. PushSubscription 2362 A push subscription is a message delivery context established between 2363 the client and a push service. A *PushSubscription* object has the 2364 following properties: 2366 o *id*: "String" (immutable; server-set) The id of the push 2367 subscription. 2369 o *deviceClientId*: "String" (immutable) An id that uniquely 2370 identifies the client + device it is running on. The purpose of 2371 this is to allow clients to identify which PushSubscription 2372 objects they created even if they lose their local state, so they 2373 can revoke or update them. This string MUST be different on 2374 different devices, and be different from other vendors. It SHOULD 2375 be easy to re-generate, not depend on persisted state. A secure 2376 hash that includes both a device id and vendor id is one way this 2377 could be achieved. 2379 o *url*: "String" (immutable) An absolute URL where the JMAP server 2380 will POST the data for the push message. This MUST begin with 2381 "https://". 2383 o *keys*: "Object|null" (immutable) Client-generated encryption 2384 keys. If supplied the server MUST use them as specified in 2385 [RFC8291] to encrypt all data sent to the push subscription. The 2386 object MUST have the following properties: 2388 * *p256dh*: the P-256 ECDH Diffie-Hellman public key as described 2389 in [RFC8291], encoded in URL-safe Base64 representation as 2390 defined in [RFC4648]. 2392 * *auth*: the authentication secret as described in [RFC8291], 2393 encoded in URL-safe base64 representation as defined in 2394 [RFC4648]. 2396 o *expires*: "UTCDate|null" The time this push subscription expires. 2397 If specified, the JMAP server MUST NOT make further requests to 2398 this resource after this time. It MAY automatically destroy the 2399 push subscription at or after this time. The server MAY choose to 2400 set an expiry if none is given by the client, or modify the expiry 2401 time given by the client to a shorter duration. 2403 o *types*: "String[]|null" A list of types the client is interested 2404 in (using the same names as the keys in the _TypeState_ object). 2405 Push events will only be sent if the data for one of these types 2406 changes. Other types are omitted from the TypeState object. If 2407 "null", changes will be pushed for all types. 2409 Clients may create a push subscription on the JMAP server, which will 2410 then make a POST request to the associated push endpoint whenever an 2411 event occurs. 2413 The POST request MUST have a content type of "application/json" and 2414 contain the UTF-8 JSON encoded _StateChange_ object as the body. The 2415 request MUST have a "TTL" header, and MAY have "Urgency" and/or 2416 "Topic" headers, as specified in section 5 of [RFC8030]. 2418 If the response code is "503" (Service Unavailable), the JMAP server 2419 MAY try again later, but may also just drop the event. If the 2420 response code is "429" (Too Many Requests) the JMAP server SHOULD 2421 attempt to reduce the frequency of pushes to that URL. Any other 2422 "4xx" or "5xx" response code MUST be considered a *permanent failure* 2423 and the push subscription SHOULD be destroyed. 2425 The use of this push endpoint conforms with the use of a push 2426 endpoint by an Application Server as defined in [RFC8030]. A client 2427 MAY use the rest of [RFC8030] in combination with its own Push Server 2428 to form a complete end-to-end solution, or MAY rely on alternative 2429 mechanisms to ensure the delivery of the pushed data after it leaves 2430 the JMAP server. 2432 The push subscription is tied to the credentials used to authenticate 2433 the API request that created it. Should these credentials expire or 2434 be revoked, the push subscription MUST be destroyed by the JMAP 2435 server. 2437 When these credentials have their own expiry (i.e. it is a session 2438 with a timeout), the server SHOULD NOT set or bound the expiry time 2439 for the push subscription given by the client, but MUST expire it 2440 when the session expires. 2442 When these credentials are not time bounded (e.g. [RFC7617] Basic 2443 Authentication), the server SHOULD set an expiry time for the push 2444 subscription if none given, and limit the expiry time if set too far 2445 in the future. This maximum expiry time MUST be at least 48 hours in 2446 the future and SHOULD be at least 7 days in the future. 2448 In the case of separate access and refresh credentials, as in 2449 [RFC6749] Oauth 2.0, the server SHOULD tie the push subscription to 2450 the validity of the refresh token rather than the access token, and 2451 behave according to whether this is time-limited or not. 2453 7.2.1. PushSubscription/get 2455 Standard _/get_ method, except it does *not* take or return an 2456 _accountId_ argument, as push subscriptions are not tied to specific 2457 accounts. It also does *not* return a _state_ argument. The _ids_ 2458 argument may be "null" to fetch all at once. 2460 As the _url_ and _keys_ properties may contain data that is private 2461 to a particular device, the values for these properties MUST NOT be 2462 returned. If the _properties_ argument is "null" or omitted, the 2463 server MUST default to all properties excluding these two. If one of 2464 them is explicitly requested, the method call MUST be rejected with a 2465 "forbidden" error. 2467 7.2.2. PushSubscription/set 2469 Standard _/set_ method except it does *not* take or return an 2470 _accountId_ argument, as push subscriptions are not tied to specific 2471 accounts. It also does *not* take an _ifInState_ argument or return 2472 _oldState_ or _newState_ arguments. 2474 The _url_ and _keys_ properties are immutable; if the client wishes 2475 to change these, it must destroy the current push subscription and 2476 create a new one. 2478 The client may update the _expires_ property to extend (or, less 2479 commonly, shorten) the lifetime of a push subscription. The server 2480 MAY modify the proposed new expiry time to enforce server-defined 2481 limits. 2483 Clients SHOULD NOT update or destroy a push subscription that they 2484 did not create (i.e. has a _deviceClientId_ that they do not 2485 recognise). 2487 7.2.3. Example 2489 A client with deviceClientId "a889-ffea-910" fetches the set of push 2490 subscriptions currently on the server, making an API request with: 2492 [[ "PushSubscription/get", { 2493 "ids": null, 2494 }, "0" ]] 2496 Which returns: 2498 [[ "PushSubscription/get", { 2499 "list": [{ 2500 "id": "e50b2c1d-9553-41a3-b0a7-a7d26b599ee1", 2501 "deviceClientId": "b37ff8001ca0", 2502 "expires": "2018-01-31T00:13:21Z", 2503 "types": [ "Todo" ] 2504 }, { 2505 "id": "f2d0aab5-e976-4e8b-ad4b-b380a5b987e4", 2506 "deviceClientId": "8980f37f6c71", 2507 "expires": "2018-02-12T05:55:00Z", 2508 "types": [ "Mailbox", "Email", "EmailDelivery" ] 2509 }], 2510 "notFound": [] 2511 }, "0" ]] 2513 Since neither of the returned push subscription objects have the 2514 client's deviceClientId, it knows it does not have a current push 2515 subscription active on the server. So it creates one, sending this 2516 request: 2518 [[ "PushSubscription/set", { 2519 "create": { 2520 "4f29": { 2521 "deviceClientId": "a889-ffea-910", 2522 "url": "https://example.com/push/?device=8980f37f6c&client=12c6d086", 2523 "types": null 2524 } 2525 } 2526 }, "0" ]] 2528 The server creates the push subscription but limits the expiry time 2529 to 7 days in the future, returning this response: 2531 [[ "PushSubscription/set", { 2532 "created": { 2533 "4f29": { 2534 "id": "043dcfa4-1dd4-41ef-9156-2c89b3b19c60", 2535 "keys": null, 2536 "expires": "2018-07-13T02:14:29Z" 2537 } 2538 } 2539 }, "0" ]] 2541 Two days later, the client updates the subscription to extend its 2542 lifetime, sending this request: 2544 [[ "PushSubscription/set", { 2545 "update": { 2546 "043dcfa4-1dd4-41ef-9156-2c89b3b19c60": { 2547 "expires": "2018-08-13T00:00:00Z" 2548 } 2549 } 2550 }, "0" ]] 2552 The server extends the expiry time, but only again to its maximum 2553 limit of 7 days in the future, returning this response: 2555 [[ "PushSubscription/set", { 2556 "updated": { 2557 "043dcfa4-1dd4-41ef-9156-2c89b3b19c60": { 2558 "expires": "2018-07-16T02:22:50Z" 2559 } 2560 } 2561 }, "0" ]] 2563 7.3. Event Source 2565 Clients that can hold open TCP connections can connect directly to 2566 the JMAP server to receive push notifications via a "text/event- 2567 stream" resource, as described in . This is a long running HTTP request down which the 2569 server can push data. 2571 When a change occurs in the data on the server, it pushes an event 2572 called *state* to any connected clients, with the _StateChange_ 2573 object as the data. 2575 The server SHOULD also send a new event id that encodes the entire 2576 server state visible to the user immediately after sending a _state_ 2577 event. When a new connection is made to the event-source endpoint, a 2578 client following the server-sent events specification [1] will send a 2579 Last-Event-ID HTTP header with the last id it saw, which the server 2580 can use to work out whether the client has missed some changes. If 2581 so, it SHOULD send these changes immediately on connection. 2583 The client MAY add a query parameter called "types", with the value 2584 being a comma-separated list of type names. If present, the server 2585 MUST only push changes for the types in this list. If omitted, 2586 changes to all types are pushed. 2588 The client MAY add a query parameter called "closeafter" with value 2589 "state" to the event-source resource URL when requesting the event- 2590 source resource. If set, the server MUST end the HTTP response after 2591 pushing a _state_ event. This can be used by clients in environments 2592 where buffering proxies prevent the pushed data from arriving 2593 immediately, or indeed at all, when operating in the usual mode. 2595 The client MAY add a query parameter called "ping", with a positive 2596 integer value representing a length of time in seconds, e.g. 2597 "ping=300". If set, the server MUST send an event called *ping* 2598 whenever this time elapses since the previous event was sent. This 2599 MUST NOT set a new event id. 2601 The server MAY modify the interval given as a query parameter to be 2602 subject to a minimum and/or maximum value. For interoperability, 2603 servers MUST NOT have a minimum allowed value higher than 30 or a 2604 maximum allowed value less than 300. 2606 The data for the ping event MUST be a JSON object containing an 2607 _interval_ property, the value (type "PositiveInt") being the 2608 interval in seconds the server is using to send pings (this may be 2609 different to the requested value if the server clamped it to be 2610 within a min/max value). 2612 Clients can monitor for the _ping_ event to help determine when the 2613 closeafter mode may be required. 2615 Refer to the JMAP Session resource section of this spec for details 2616 on how to get the URL for the event-source resource. Requests to the 2617 resource MUST be authenticated. 2619 A client MAY hold open multiple connections to the event-source 2620 resource, although it SHOULD try to use a single connection for 2621 efficiency. 2623 8. Security considerations 2625 8.1. Transport confidentiality 2627 All HTTP requests MUST use [RFC5246] TLS (https) transport to ensure 2628 the confidentiality of data sent and received via JMAP. Clients MUST 2629 validate TLS certificate chains to protect against man-in-the-middle 2630 attacks. 2632 8.2. Authentication scheme 2634 A number of HTTP authentication schemes have been standardised 2635 (). Servers should take care to assess the security 2637 characteristics of different schemes in relation to their needs when 2638 deciding what to implement. 2640 If offering the Basic authentication scheme, services are strongly 2641 recommended to not allow a user's regular password but require 2642 generation of a unique "app password" via some external mechanism for 2643 each client they wish to connect. This allows connections from 2644 different devices to be differentiated by the server, and access to 2645 be individually revoked. 2647 8.3. Service autodiscovery 2649 Unless secured by something like DNSSEC, autodiscovery of server 2650 details is vulnerable to a DNS poisoning attack leading to the client 2651 talking to an attacker's server instead of the real JMAP server. The 2652 attacker may then man-in-the-middle requests and depending on the 2653 authentication scheme, steal credentials to generate its own 2654 requests. 2656 Clients that do not support SRV lookups are likely to try just using 2657 the "/.well-known/jmap" path directly against the domain of the 2658 username over HTTPS. Servers SHOULD ensure this path resolves or 2659 redirects to the correct JMAP Session resource to allow this to work. 2661 If this is not feasible, servers MUST ensure this path cannot be 2662 controlled by an attacker, as again it may be used to steal 2663 credentials. 2665 8.4. JSON parsing 2667 The security considerations of [RFC7159] apply to the use of JSON as 2668 the data interchange format. 2670 8.5. Denial of service 2672 A small request may result in a very large response, and require 2673 considerable work on the server if resource limits are not enforced. 2674 JMAP provides mechanisms for advertising and enforcing a wide variety 2675 of limits for mitigating this threat, including limits on number of 2676 objects fetched in a single method call, number of methods in a 2677 single request, number of concurrent requests, etc. 2679 JMAP servers MUST implement sensible limits to mitigate against 2680 resource exhaustion attacks. 2682 8.6. Push encryption 2684 When data changes, a small object is pushed with the new state 2685 strings for the types that have changed. While the data here is 2686 minimal, a passive man-in-the-middle attacker may be able to gain 2687 useful information. To ensure confidentiality, if the push is sent 2688 via a third party outside of the control of the client and JMAP 2689 server the client MUST specify encryption keys when establishing the 2690 PushSubscription. 2692 The privacy and security considerations of [RFC8030] and [RFC8291] 2693 also all apply to the use of the PushSubscription mechanism. 2695 9. IANA considerations 2697 9.1. Assignment of jmap service name 2699 IANA will assign the 'jmap' service name in the 'Service Name and 2700 Transport Protocol Port Number Registry' [RFC6335]. 2702 Service Name: jmap 2704 Transport Protocol(s): tcp 2706 Assignee: IESG 2708 Contact: IETF Chair 2709 Description: JSON Meta Application Protocol 2711 Reference: this document 2713 Assignment Notes: this service name was previously assigned under the 2714 name _JSON Mail Access Protocol_. This will be de-assigned and re- 2715 assigned with the approval of the previous assignee. 2717 9.2. Registration of well-known URI suffix for JMAP 2719 IANA will register the following well-known URI suffix for JMAP as 2720 described in [RFC5785]: 2722 URI Suffix: jmap 2724 Change Controller: IETF 2726 Specification Document: this document, section 2.2. 2728 9.3. Registration of the jmap URN sub-namespace 2730 IANA will register the following URN sub-namespace in the "IETF URN 2731 Sub-namespace for Registered Protocol Parameter Identifiers" registry 2732 as described in [RFC3553]. 2734 Registered Parameter Identifier: jmap 2736 Reference: this document, next section 2738 IANA Registry Reference: {insert IANA registry URL for registry in 2739 next section, upon approval} 2741 9.4. Creation of "JMAP Capabilities" registry 2743 IANA will create a registry for JMAP capabilities as described in 2744 section 2. JMAP capabilities are advertised in the _capabilities_ 2745 property of the _JMAP Session_ resource. They are used to extend the 2746 functionality of a JMAP server. A capability is referenced by a URI. 2747 The JMAP capability URI can be a URN starting with 2748 "urn:ietf:params:jmap:" plus a unique suffix which is the index value 2749 in the jmap URN sub-namespace. Registration of a JMAP capability 2750 with another form of URI has no impact on the jmap URN sub-namespace. 2752 This registry follows the expert review process unless the "intended 2753 use" field is _common_ or _placeholder_ in which case registration 2754 follows the specification required process. 2756 A JMAP capability registration can have an intended use of _common_, 2757 _placeholder_, _limited_, or _obsolete_. IANA will list common use 2758 registrations prominently and separately from those with other 2759 intended use values. 2761 The JMAP capability registration procedure is not a formal standards 2762 process, but rather an administrative procedure intended to allow 2763 community comment and sanity checking without excessive time delay. 2765 A _placeholder_ registration reserves part of the jmap urn namespace 2766 for another purpose but is typically not included in the 2767 _capabilities_ property of the _JMAP Session_ resource. 2769 9.4.1. Preliminary community review 2771 Notice of a potential JMAP common use registration SHOULD be sent to 2772 the jmap@ietf.org mailing list for review. This mailing list is 2773 appropriate to solicit community feedback on a proposed JMAP 2774 capability. Registrations that are not intended for common use MAY 2775 be sent to the list for review as well; doing so is entirely 2776 OPTIONAL, but is encouraged. 2778 The intent of the public posting to this list is to solicit comments 2779 and feedback on the choice of capability name, the unambiguity of the 2780 specification document, and a review of any interoperability or 2781 security considerations. The submitter may submit a revised 2782 registration proposal or abandon the registration completely and at 2783 any time. 2785 9.4.2. Submit request to IANA 2787 Registration requests can be sent to iana@iana.org. 2789 9.4.3. Designated expert review 2791 For a limited use registration, the designated expert's (DE) primary 2792 concern is preventing name collisions and encouraging the submitter 2793 to document security and privacy considerations; a published 2794 specification is not required. For a common use registration, the DE 2795 is expected to confirm that suitable documentation as described in 2796 [RFC8126], Section 4.6, is available. The DE should also verify the 2797 capability does not conflict with work that is active or already 2798 published within the IETF. 2800 Before a period of 30 days has passed, the DE will either approve or 2801 deny the registration request and publish a notice of the decision to 2802 the JMAP WG mailing list or its successor, as well as informing IANA. 2803 A denial notice must be justified by an explanation, and in the cases 2804 where it is possible, concrete suggestions on how the request can be 2805 modified so as to become acceptable should be provided. 2807 9.4.4. Change procedures 2809 Once a JMAP capability has been published by the IANA, the change 2810 controller may request a change to its definition. The same 2811 procedure that would be appropriate for the original registration 2812 request is used to process a change request. 2814 JMAP capability registrations may not be deleted; capabilities that 2815 are no longer believed appropriate for use can be declared obsolete 2816 by a change to their "intended use" field; such capabilities will be 2817 clearly marked in the lists published by the IANA. 2819 Significant changes to a capability's definition should be requested 2820 only when there are serious omissions or errors in the published 2821 specification. When review is required, a change request may be 2822 denied if it renders entities that were valid under the previous 2823 definition invalid under the new definition. 2825 The owner of a JMAP capability may pass responsibility to another 2826 person or agency by informing the IANA; this can be done without 2827 discussion or review. 2829 The IESG may reassign responsibility for a JMAP capability. The most 2830 common case of this will be to enable changes to be made to 2831 capabilities where the author of the registration has died, moved out 2832 of contact, or is otherwise unable to make changes that are important 2833 to the community. 2835 9.4.5. JMAP Capabilities registry template: 2837 Capability name: (see capability property in section 2) 2839 Specification document: 2841 Intended use: (one of common, limited, or obsolete) 2843 Change controller: (_IETF_ for standards-track/BCP RFCs) 2845 Security and privacy considerations: 2847 9.4.6. Initial registration for JMAP core 2849 Capability Name: "urn:ietf:params:jmap:core" 2851 Specification document: this document, section 2 2852 Intended use: common 2854 Change Controller: IETF 2856 Security and privacy considerations: this document, section 8. 2858 9.4.7. Registration for JMAP error placeholder in JMAP capabilities 2859 registry 2861 Capability Name: `urn:ietf:params:jmap:error:' 2863 Specification document: this document, next section. 2865 Intended use: placeholder 2867 Change Controller: IETF 2869 Security and privacy considerations: this document, section 8. 2871 9.5. Creation of "JMAP Error Codes" registry 2873 IANA will create a registry for JMAP error codes. JMAP error codes 2874 appear in the "type" member of a JSON problem details object (as 2875 described in section 3.5.1), in the "type" member in a JMAP error 2876 object (as described in section 3.5.2), or the "type" member of a 2877 JMAP method-specific error object (such as SetError in section 5.3). 2878 When used in a problem details object, the prefix 2879 'urn:ietf:params:jmap:error:' is always included, and when used in 2880 JMAP objects, the prefix is always omitted. 2882 This registry follows the expert review process. Preliminary 2883 community review for this registry follows the same procedures as the 2884 JMAP capabilities registry but is optional. The change procedures 2885 for this registry are the same as the change procedures for the JMAP 2886 capabilities registry. 2888 9.5.1. Designated expert review 2890 The designated expert should review the following aspects of the 2891 registration: 2893 1. Verify the error code does not conflict with existing names. 2895 2. Verify the error code follows the syntax limitations (does not 2896 require URI encoding). 2898 3. Encourage the error code to follow the naming convention of 2899 previously registered errors. 2901 4. Encourage description of client behaviors that are recommended in 2902 response to the error code. These may distinguish the error code 2903 from other error codes. 2905 5. Encourage description of when the server should issue the error 2906 as opposed to some other error code. 2908 6. Encourage the submitter to note any security considerations 2909 associated with the error, if any. For example, an error code 2910 that might disclose existence of data the authenticated user does 2911 not have permission to know about. 2913 Steps 3-6 are meant to promote a higher-quality registry. However, 2914 the expert is encouraged to approve any registration that would not 2915 actively harm JMAP interoperability to make this a relatively light- 2916 weight process. 2918 9.5.2. JMAP Error Codes registry template: 2920 JMAP Error Code: 2922 Intended use: (one of _common_, _limited_, _obsolete_) 2924 Change Controller: (_IETF_ for standards-track/BCP RFCs) 2926 Description or Reference: 2928 9.5.3. Initial JMAP Error Codes registry 2930 +------------------------------+---------+------------+-------------+ 2931 | JMAP Error Code | Intende | Change | Description | 2932 | | d Use | Controller | or | 2933 | | | | Reference | 2934 +------------------------------+---------+------------+-------------+ 2935 | accountNotFound | common | IETF | RFC XXXX | 2936 | | | | section | 2937 | | | | 3.5.2 | 2938 | accountNotSupportedByMethod | common | IETF | RFC XXXX | 2939 | | | | section | 2940 | | | | 3.5.2 | 2941 | accountReadOnly | common | IETF | RFC XXXX | 2942 | | | | section | 2943 | | | | 3.5.2 | 2944 | anchorNotFound | common | IETF | RFC XXXX | 2945 | | | | section 5.5 | 2946 | alreadyExists | common | IETF | RFC XXXX | 2947 | | | | section 5.4 | 2948 | cannotCalculateChanges | common | IETF | RFC XXXX | 2949 | | | | sections | 2950 | | | | 5.2 and 5.6 | 2951 | forbidden | common | IETF | RFC XXXX | 2952 | | | | sections | 2953 | | | | 3.5.2, 5.3, | 2954 | | | | and 7.2.1 | 2955 | fromAccountNotFound | common | IETF | RFC XXXX | 2956 | | | | sections | 2957 | | | | 5.4 and 6.3 | 2958 | fromAccountNotSupportedByMet | common | IETF | RFC XXXX | 2959 | hod | | | section 5.4 | 2960 | invalidArguments | common | IETF | RFC XXXX | 2961 | | | | section | 2962 | | | | 3.5.2 | 2963 | invalidPatch | common | IETF | RFC XXXX | 2964 | | | | section 5.3 | 2965 | invalidProperties | common | IETF | RFC XXXX | 2966 | | | | section 5.3 | 2967 | notFound | common | IETF | RFC XXXX | 2968 | | | | section 5.3 | 2969 | notJSON | common | IETF | RFC XXXX | 2970 | | | | section | 2971 | | | | 3.5.1 | 2972 | notRequest | common | IETF | RFC XXXX | 2973 | | | | section | 2974 | | | | 3.5.1 | 2975 | overQuota | common | IETF | RFC XXXX | 2976 | | | | section 5.3 | 2977 | rateLimit | common | IETF | RFC XXXX | 2978 | | | | section 5.3 | 2979 | requestTooLarge | common | IETF | RFC XXXX | 2980 | | | | sections | 2981 | | | | 5.1 and 5.3 | 2982 | invalidResultReference | common | IETF | RFC XXXX | 2983 | | | | section | 2984 | | | | 3.5.2 | 2985 | serverFail | common | IETF | RFC XXXX | 2986 | | | | section | 2987 | | | | 3.5.2 | 2988 | serverPartialFail | limited | IETF | RFC XXXX | 2989 | | | | section | 2990 | | | | 3.5.2 | 2991 | serverUnavailable | common | IETF | RFC XXXX | 2992 | | | | section | 2993 | | | | 3.5.2 | 2994 | singleton | common | IETF | RFC XXXX | 2995 | | | | section 5.3 | 2996 | stateMismatch | common | IETF | RFC XXXX | 2997 | | | | section 5.3 | 2998 | toAccountNotFound | common | IETF | RFC XXXX | 2999 | | | | sections | 3000 | | | | 5.4 and 6.3 | 3001 | toAccountNotSupportedByMetho | common | IETF | RFC XXXX | 3002 | d | | | section 5.4 | 3003 | tooLarge | common | IETF | RFC XXXX | 3004 | | | | section 5.3 | 3005 | tooManyChanges | common | IETF | RFC XXXX | 3006 | | | | section 5.6 | 3007 | unknownCapability | common | IETF | RFC XXXX | 3008 | | | | section | 3009 | | | | 3.5.1 | 3010 | unknownMethod | common | IETF | RFC XXXX | 3011 | | | | section | 3012 | | | | 3.5.2 | 3013 | unsupportedFilter | common | IETF | RFC XXXX | 3014 | | | | section 5.5 | 3015 | unsupportedSort | common | IETF | RFC XXXX | 3016 | | | | section 5.5 | 3017 | willDestroy | common | IETF | RFC XXXX | 3018 | | | | section 5.3 | 3019 +------------------------------+---------+------------+-------------+ 3021 10. References 3023 10.1. Normative References 3025 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 3026 Requirement Levels", BCP 14, RFC 2119, 3027 DOI 10.17487/RFC2119, March 1997, 3028 . 3030 [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for 3031 specifying the location of services (DNS SRV)", RFC 2782, 3032 DOI 10.17487/RFC2782, February 2000, 3033 . 3035 [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: 3036 Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, 3037 . 3039 [RFC3553] Mealling, M., Masinter, L., Hardie, T., and G. Klyne, "An 3040 IETF URN Sub-namespace for Registered Protocol 3041 Parameters", BCP 73, RFC 3553, DOI 10.17487/RFC3553, June 3042 2003, . 3044 [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 3045 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November 3046 2003, . 3048 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 3049 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 3050 . 3052 [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet 3053 Application Protocol Collation Registry", RFC 4790, 3054 DOI 10.17487/RFC4790, March 2007, 3055 . 3057 [RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation 3058 Algorithm", RFC 5051, DOI 10.17487/RFC5051, October 2007, 3059 . 3061 [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security 3062 (TLS) Protocol Version 1.2", RFC 5246, 3063 DOI 10.17487/RFC5246, August 2008, 3064 . 3066 [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, 3067 DOI 10.17487/RFC5322, October 2008, 3068 . 3070 [RFC5785] Nottingham, M. and E. Hammer-Lahav, "Defining Well-Known 3071 Uniform Resource Identifiers (URIs)", RFC 5785, 3072 DOI 10.17487/RFC5785, April 2010, 3073 . 3075 [RFC6186] Daboo, C., "Use of SRV Records for Locating Email 3076 Submission/Access Services", RFC 6186, 3077 DOI 10.17487/RFC6186, March 2011, 3078 . 3080 [RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S. 3081 Cheshire, "Internet Assigned Numbers Authority (IANA) 3082 Procedures for the Management of the Service Name and 3083 Transport Protocol Port Number Registry", BCP 165, 3084 RFC 6335, DOI 10.17487/RFC6335, August 2011, 3085 . 3087 [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., 3088 and D. Orchard, "URI Template", RFC 6570, 3089 DOI 10.17487/RFC6570, March 2012, 3090 . 3092 [RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", 3093 RFC 6749, DOI 10.17487/RFC6749, October 2012, 3094 . 3096 [RFC6764] Daboo, C., "Locating Services for Calendaring Extensions 3097 to WebDAV (CalDAV) and vCard Extensions to WebDAV 3098 (CardDAV)", RFC 6764, DOI 10.17487/RFC6764, February 2013, 3099 . 3101 [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type 3102 Specifications and Registration Procedures", BCP 13, 3103 RFC 6838, DOI 10.17487/RFC6838, January 2013, 3104 . 3106 [RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., 3107 "JavaScript Object Notation (JSON) Pointer", RFC 6901, 3108 DOI 10.17487/RFC6901, April 2013, 3109 . 3111 [RFC7159] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 3112 Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March 3113 2014, . 3115 [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 3116 Protocol (HTTP/1.1): Message Syntax and Routing", 3117 RFC 7230, DOI 10.17487/RFC7230, June 2014, 3118 . 3120 [RFC7235] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 3121 Protocol (HTTP/1.1): Authentication", RFC 7235, 3122 DOI 10.17487/RFC7235, June 2014, 3123 . 3125 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 3126 DOI 10.17487/RFC7493, March 2015, 3127 . 3129 [RFC7617] Reschke, J., "The 'Basic' HTTP Authentication Scheme", 3130 RFC 7617, DOI 10.17487/RFC7617, September 2015, 3131 . 3133 [RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP 3134 APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016, 3135 . 3137 [RFC8030] Thomson, M., Damaggio, E., and B. Raymor, Ed., "Generic 3138 Event Delivery Using HTTP Push", RFC 8030, 3139 DOI 10.17487/RFC8030, December 2016, 3140 . 3142 [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for 3143 Writing an IANA Considerations Section in RFCs", BCP 26, 3144 RFC 8126, DOI 10.17487/RFC8126, June 2017, 3145 . 3147 [RFC8291] Thomson, M., "Message Encryption for Web Push", RFC 8291, 3148 DOI 10.17487/RFC8291, November 2017, 3149 . 3151 10.2. URIs 3153 [1] https://html.spec.whatwg.org/multipage/server-sent-events.html 3155 Authors' Addresses 3157 Neil Jenkins 3158 FastMail 3159 PO Box 234, Collins St West 3160 Melbourne VIC 8007 3161 Australia 3163 Email: neilj@fastmailteam.com 3164 URI: https://www.fastmail.com 3166 Chris Newman 3167 Oracle 3168 440 E. Huntington Dr., Suite 400 3169 Arcadia CA 91006 3170 United States of America 3172 Email: chris.newman@oracle.com