JMAP                                                   N.M.

Internet Engineering Task Force (IETF)                   N. Jenkins, Ed.
Internet-Draft
Request for Comments: 9610                                      Fastmail
Intended status:
Category: Standards Track                             7 June 2024
Expires: 9                                  December 2024

                           JMAP
ISSN: 2070-1721

           JSON Meta Application Protocol (JMAP) for Contacts
                      draft-ietf-jmap-contacts-10

Abstract

   This document specifies a data model for synchronising contacts contact data
   with a server using JMAP. the JSON Meta Application Protocol (JMAP).

Status of This Memo

   This Internet-Draft is submitted in full conformance with the
   provisions of BCP 78 and BCP 79.

   Internet-Drafts are working documents an Internet Standards Track document.

   This document is a product of the Internet Engineering Task Force
   (IETF).  Note that other groups may also distribute
   working documents as Internet-Drafts.  The list  It represents the consensus of current Internet-
   Drafts is at https://datatracker.ietf.org/drafts/current/.

   Internet-Drafts are draft documents valid the IETF community.  It has
   received public review and has been approved for a maximum publication by the
   Internet Engineering Steering Group (IESG).  Further information on
   Internet Standards is available in Section 2 of six months RFC 7841.

   Information about the current status of this document, any errata,
   and how to provide feedback on it may be updated, replaced, or obsoleted by other documents obtained at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

   This Internet-Draft will expire on 9 December 2024.
   https://www.rfc-editor.org/info/rfc9610.

Copyright Notice

   Copyright (c) 2024 IETF Trust and the persons identified as the
   document authors.  All rights reserved.

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents (https://trustee.ietf.org/
   license-info)
   (https://trustee.ietf.org/license-info) in effect on the date of
   publication of this document.  Please review these documents
   carefully, as they describe your rights and restrictions with respect
   to this document.  Code Components extracted from this document must
   include Revised BSD License text as described in Section 4.e of the
   Trust Legal Provisions and are provided without warranty as described
   in the Revised BSD License.

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   2
     1.1.  Notational Conventions  . . . . . . . . . . . . . . . . .   3
     1.2.  Terminology . . . . . . . . . . . . . . . . . . . . . . .   3
     1.3.  Data Model Overview . . . . . . . . . . . . . . . . . . .   3
     1.4.  Addition to the Capabilities Object . . . . . . . . . . .   3
       1.4.1.  urn:ietf:params:jmap:contacts . . . . . . . . . . . .   3
   2.  AddressBooks  . . . . . . . . . . . . . . . . . . . . . . . .   4
     2.1.  AddressBook/get . . . . . . . . . . . . . . . . . . . . .   6
     2.2.  AddressBook/changes . . . . . . . . . . . . . . . . . . .   6
     2.3.  AddressBook/set . . . . . . . . . . . . . . . . . . . . .   6
   3.  ContactCards  . . . . . . . . . . . . . . . . . . . . . . . .   7
     3.1.  ContactCard/get . . . . . . . . . . . . . . . . . . . . .   8
     3.2.  ContactCard/changes . . . . . . . . . . . . . . . . . . .   8
     3.3.  ContactCard/query . . . . . . . . . . . . . . . . . . . .   8
       3.3.1.  Filtering . . . . . . . . . . . . . . . . . . . . . .   8
       3.3.2.  Sorting . . . . . . . . . . . . . . . . . . . . . . .  11
     3.4.  ContactCard/queryChanges  . . . . . . . . . . . . . . . .  11
     3.5.  ContactCard/set . . . . . . . . . . . . . . . . . . . . .  11
     3.6.  ContactCard/copy  . . . . . . . . . . . . . . . . . . . .  11
   4.  Examples  . . . . . . . . . . . . . . . . . . . . . . . . . .  12
     4.1.  Fetching initial data . . . . . . . . . . . . . . . . . .  12 Initial Data
     4.2.  Changing the default address book . . . . . . . . . . . .  14 Default Address Book
   5.  Internationalisation Considerations . . . . . . . . . . . . .  14
   6.  Security Considerations . . . . . . . . . . . . . . . . . . .  15
   7.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  15
     7.1.  JMAP capability registration Capability Registration for "contacts" . . . . . . .  15
     7.2.  JMAP Data Type Registration for "AddressBook" . . . . . .  15
     7.3.  JMAP Data Type Registration for "ContactCard" . . . . . .  16
     7.4.  JMAP Error Codes Registry . . . . . . . . . . . . . . . .  16
       7.4.1.  addressBookHasContents  . . . . . . . . . . . . . . .  16
     7.5.  JSContact Property Registrations  . . . . . . . . . . . .  16
       7.5.1.  id  . . . . . . . . . . . . . . . . . . . . . . . . .  16
       7.5.2.  addressBookIds  . . . . . . . . . . . . . . . . . . .  17
       7.5.3.  blobId  . . . . . . . . . . . . . . . . . . . . . . .  17
   8.  References
     8.1.  Normative References  . . . . . . . . . . . . . . . . . . . .  17
   9.
     8.2.  Informative References  . . . . . . . . . . . . . . . . . . .  18
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . .  18

1.  Introduction

   JMAP ([RFC8620]

   The JSON Meta Application Protocol) Protocol (JMAP) [RFC8620] is a generic
   protocol for synchronising data, such as mail, calendars calendars, or
   contacts, between a client and a server.  It is optimised for mobile
   and web
   environments, environments and aims to provide a consistent interface to
   different data types.

   This specification defines a data model for synchronising contacts
   between a client and a server using JMAP.

1.1.  Notational Conventions

   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
   "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
   "OPTIONAL" in this document are to be interpreted as described in
   BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
   capitals, as shown here.

   Type signatures, examples examples, and property descriptions in this document
   follow the conventions established in Section 1.1 of [RFC8620].  The
   Id, UnsignedInt, and UTCDate data types defined in Sections 1.2, 1.3,
   and 1.4 of [RFC8620] are also used in this document.

1.2.  Terminology

   The same terminology is used in this document as in the core JMAP
   specification, see [RFC8620], specification (see
   Section 1.6. 1.6 of [RFC8620]) is also used in this document.

   The terms AddressBook and ContactCard (with these specific
   capitalizations) are used to refer to the data types defined in this
   document and instances of those data types.

1.3.  Data Model Overview

   An Account (see [RFC8620], Section 1.6.2) 1.6.2 of [RFC8620]) with support for the
   contacts
   contact data model contains zero or more AddressBook objects, which
   is a named collection of zero or more ContactCards.  A ContactCard is
   a representation of a person, company, or other entity, or a group of such entities,
   entities in JSContact Card format, as defined in Section 2 of
   [RFC9553].  Each ContactCard belongs to one or more AddressBooks.

   In servers with support for JMAP Sharing [I-D.ietf-jmap-sharing], [RFC9670], users may see and
   configure sharing of contact data with others.  Sharing permissions
   are managed per AddressBook.

1.4.  Addition to the Capabilities Object

   The capabilities object is returned as part of the JMAP Session
   object; see [RFC8620], Section 2. 2 of [RFC8620].  This document defines one
   additional capability URI.

1.4.1.  urn:ietf:params:jmap:contacts

   This represents support for the AddressBook and ContactCard data
   types and associated API methods.  The value of this property in the
   JMAP Session capabilities "capabilities" property is an empty object.

   The value of this property in an account's accountCapabilities "accountCapabilities"
   property is an object that MUST contain the following information on
   server capabilities and permissions for that account:

   *

   *maxAddressBooksPerCard*: UnsignedInt|null
      The maximum number of AddressBooks (see Section 2) that can be
      assigned to a single ContactCard object (see Section 3).  This
      MUST be an integer >= 1, or null for no limit (or rather, the
      limit is always the number of AddressBooks in the account).
   *

   *mayCreateAddressBook*: Boolean
      The user may create an AddressBook in this account if, and only
      if, this is true.

2.  AddressBooks

   An AddressBook is a named collection of ContactCards.  All
   ContactCards are associated with one or more AddressBook.

   A AddressBooks.

   An *AddressBook* object has the following properties:

   *

   *id*: Id (immutable; server-set)
      The id of the AddressBook.

   *

   *name*: String
      The user-visible name of the AddressBook.  This MUST NOT be the
      empty string and MUST NOT be greater than 255 octets in size when
      encoded as UTF-8.

   *

   *description*: String|null (default: null)
      An optional longer-form long-form description of the AddressBook, to provide AddressBook that provides
      context in shared environments where users need more than just the
      name.

   *

   *sortOrder*: UnsignedInt (default: 0)
      Defines the sort order of AddressBooks when presented in the
      client's UI, UI so it is consistent between devices.  The number MUST
      be an integer in the range 0 <= sortOrder < 2^(31.) 2^31.

      An AddressBook with a lower order is to be displayed before a
      AddressBook with a higher order in any list of AddressBooks in the
      client's UI.  AddressBooks with equal order should be sorted in
      alphabetical order by name.  The sorting should take into account
      locale-specific character order convention.

   *

   *isDefault*: Boolean (server-set)
      This SHOULD be true for exactly one AddressBook in any account, account and
      MUST NOT be true for more than one AddressBook within an account.
      The default AddressBook should be used by clients whenever they
      need to choose an AddressBook for the user within this account, account and
      they do not have any other information on which to make a choice.
      For example, if the user creates a new contact card, the client
      may automatically set the card as belonging to the default
      AddressBook from the user's primary account.

   *

   *isSubscribed*: Boolean
      True if the user has indicated they wish to see this AddressBook
      in their client.  This SHOULD default to false for AddressBooks in
      shared accounts that the user has access to and true for any new
      AddressBooks created by the user themself.

      If false, the AddressBook and its contents SHOULD only be
      displayed when the user explicitly requests it or to it.  The UI may offer it for
      to the user the option of subscribing to subscribe to.

   * it.

   *shareWith*: Id[AddressBookRights]|null (default: null)
      A map of the Principal id (Section 2 of [I-D.ietf-jmap-sharing]) id [RFC9670]) to rights for principals
      Principals this AddressBook is shared with.  The
      principal Principal to
      which this AddressBook belongs MUST NOT be in this set.  This is
      null if the AddressBook is not shared with anyone, anyone or if the server
      does not support [I-D.ietf-jmap-sharing]. [RFC9670].  The value may be modified only if the
      user has the mayShare "mayShare" right.  The account id for the principals Principals
      may be found in the urn:ietf:params:jmap:principals:owner
      capability of the Account to which the AddressBook belongs.

   *

   *myRights*: AddressBookRights (server-set)
      The set of access rights the user has in relation to this
      AddressBook.

   An *AddressBookRights* object has the following properties:

   *

   *mayRead*: Boolean
      The user may fetch the ContactCards in this AddressBook.
   *

   *mayWrite*: Boolean
      The user may create, modify modify, or destroy all ContactCards in this
      AddressBook, or move them to or from this AddressBook.
   *

   *mayShare*: Boolean
      The user may modify the "shareWith" property for this AddressBook.

   *

   *mayDelete*: Boolean
      The user may delete the AddressBook itself.

2.1.  AddressBook/get

   This is a standard "/get" method as described in [RFC8620], Section 5.1. 5.1 of
   [RFC8620].  The _ids_ "ids" argument may be null to fetch all at once.

2.2.  AddressBook/changes

   This is a standard "/changes" method as described in [RFC8620], Section 5.2. 5.2 of
   [RFC8620].

2.3.  AddressBook/set

   This is a standard "/set" method as described in [RFC8620], Section 5.3 of
   [RFC8620], but with the following additional request argument:

   * arguments:

   *onDestroyRemoveContents*: Boolean (default: false)
      If false, any attempt to destroy an AddressBook that still has a
      ContactCard in it will be rejected with an addressBookHasContents
      "addressBookHasContents" SetError.  If true, any ContactCard that
      is in the AddressBook will be removed from it, and if such a
      ContactCard does not belong to any other AddressBook AddressBook, it will be
      destroyed.

   *

   *onSuccessSetIsDefault*: Id|null
      If an id is given, and all creates, updates updates, and destroys (if any)
      succeed without error, the server will try to set this AddressBook
      as the default.  (For references to AddressBook creations, this is
      equivalent to a creation-reference, so the id will be the creation
      id prefixed with a "#".)

   If the id is not found, found or if the change is not permitted by the
   server for policy reasons, it MUST be ignored and the currently current default
   AddressBook (if any) will remain as such.  No error is returned to
   the client in this case.

   As per [RFC8620], Section 5.3, 5.3 of [RFC8620], if the default AddressBook is
   successfully changed, any changed objects MUST be reported in either
   the "created" or "updated" argument in the response as appropriate,
   with the server-set value included.

   The "shareWith" property may only be set by users that have the
   mayShare
   "mayShare" right.  When modifying the shareWith "shareWith" property, the user
   cannot give a right to a principal Principal if the principal Principal did not already
   have that right and the user making the change also does not have
   that right.  Any attempt to do so MUST be rejected with a forbidden "forbidden"
   SetError.

   Users can subscribe or unsubscribe to an AddressBook by setting the
   "isSubscribed" property.  The server MAY forbid users from
   subscribing to certain AddressBooks even though they have permission
   to see them, rejecting the update with a forbidden "forbidden" SetError.

   The following extra SetError type is defined:

   For defined for "destroy":

   *

   *addressBookHasContents*:  The AddressBook has at least one
      ContactCard assigned to it, it and the "onDestroyRemoveContents"
      argument was false.

3.  ContactCards

   A *ContactCard* object contains information about a person, company,
   or other entity, or represents a group of such entities.  It is a
   JSContact Card object, object as defined in Section 2 of [RFC9553], [RFC9553] with the
   following additional properties:

   *

   *id*: Id (immutable; server-set)
      The id of the ContactCard.  The id "id" property MAY be different to
      the ContactCard's "uid" property (as defined in Section 2.1.9 of
      [RFC9553]).  However  However, there MUST NOT be more than one ContactCard
      with the same "uid" uid in an Account.
   *

   *addressBookIds*: Id[Boolean]
      The set of AddressBook ids that this ContactCard belongs to.  A
      card MUST belong to at least one AddressBook at all times (until
      it is destroyed).  The set is represented as an object, with each
      key being an AddressBook id.  The value for each key in the object
      MUST be true.

   For any Media object in the card (see Section 2.6.4 of [RFC9553]), a
   new property is defined:

   *

   *blobId*: Id
      An id for the Blob representing the binary contents of the
      resource.

   When returning ContactCards, any Media with a URI that uses the data
   "data:" URL scheme ([RFC2397]) [RFC2397] SHOULD return a blobId "blobId" property and
   omit the
   uri "uri" property, as this lets clients load the (potentially
   large) image file only when needed, needed and avoids the overhead of Base64
   encoding.  The "mediaType" property MUST also be set.  Similarly,
   when creating or updating a ContactCard, clients MAY send a blobId "blobId"
   instead of the
   uri "uri" property for a Media object.

   A contact card with a "kind" property equal to "group" represents a
   group of contacts.  Clients often present these separately from other
   contact cards.  The "members" property, as defined in Section 2.1.6
   of [RFC9553], contains a set of UIDs uids (as defined in Section 2.1.9 of
   [RFC9553]) for other contacts that are the members of this group.
   Clients should consider the group to contain any ContactCard with a
   matching UID, uid from any account they have access to with that has support
   for the urn:ietf:params:jmap:contacts capability.  UIDs  Any uid that
   cannot be found SHOULD be ignored but preserved.  For example,
   suppose a user adds contacts from a shared address book to their
   private group, then temporarily loses access to this address book.
   The UIDs uids cannot be
   resolved resolved, so the contacts will disappear from the
   group.  However, if they are given permission to access the data again
   again, the UIDs uids will be found and the contacts will reappear.

3.1.  ContactCard/get

   This is a standard "/get" method as described in [RFC8620], Section 5.1. 5.1 of
   [RFC8620].

3.2.  ContactCard/changes

   This is a standard "/changes" method as described in [RFC8620], Section 5.2. 5.2 of
   [RFC8620].

3.3.  ContactCard/query

   This is a standard "/query" method as described in [RFC8620], Section 5.5. 5.5 of
   [RFC8620].

3.3.1.  Filtering

   A *FilterCondition* object has the following properties, any of which
   may be omitted:

   *

   *inAddressBook*: Id
      An AddressBook id.  A card must be in this address book to match
      the condition.
   *

   *uid*: String
      A card must have this string exactly as its uid (as defined in
      Section 2.1.9 of [RFC9553]) to match.
   *

   *hasMember*: String
      A card must have a "members" property (as defined in Section 2.1.6
      of [RFC9553]) that contains this string as one of the uids in the
      set to match.
   *

   *kind*: String
      A card must have a kind "kind" property (as defined in Section 2.1.4 of
      [RFC9553]) that equals this string exactly to match.
   *

   *createdBefore*: UTCDate
      The "created" date-time of the ContactCard (as defined in
      Section 2.1.3 of [RFC9553]) must be before this date-time to match
      the condition.
   *

   *createdAfter*: UTCDate
      The "created" date-time of the ContactCard (as defined in
      Section 2.1.3 of [RFC9553]) must be the same or after this date-
      time to match the condition.
   *

   *updatedBefore*: UTCDate
      The "updated" date-time of the ContactCard (as defined in
      Section 2.1.10 of [RFC9553]) must be before this date-time to
      match the condition.
   *

   *updatedAfter*: UTCDate
      The "updated" date-time of the ContactCard (as defined in
      Section 2.1.10 of [RFC9553]) must be the same or after this date-
      time to match the condition.
   *

   *text*: String
      A card matches this condition if the text matches with text in the
      card.
   *

   *name*: String
      A card matches this condition if the value of any NameComponent in
      the "name" property, property or the "full" property in the "name" property
      of the card, card (as defined in Section 2.2.1.2 of [RFC9553]) matches
      the value.
   *

   *name/given*: String
      A card matches this condition if the value of a NameComponent with
      kind "given" inside the "name" property of the card (as defined in
      Section 2.2.1.2 of [RFC9553]) matches the value.
   *

   *name/surname*: String
      A card matches this condition if the value of a NameComponent with
      kind "surname" inside the "name" property of the card (as defined
      in Section 2.2.1.2 of [RFC9553]) matches the value.
   *

   *name/surname2*: String
      A card matches this condition if the value of a NameComponent with
      kind "surname2" inside the "name" property of the card (as defined
      in Section 2.2.1.2 of [RFC9553]) matches the value.
   *

   *nickname*: String
      A card matches this condition if the "name" of any Nickname in the
      "nicknames" property of the card (as defined in Section 2.2.2 of
      [RFC9553]) matches the value.
   *

   *organization*: String
      A card matches this condition if the "name" of any Organization in
      the "organizations" property of the card (as defined in
      Section 2.2.3 of [RFC9553]) matches the value.
   *

   *email*: String
      A card matches this condition if the "address" or "label" of any
      EmailAddress in the "emails" property of the card (as defined in
      Section 2.3.1 of [RFC9553]) matches the value.
   *

   *phone*: String
      A card matches this condition if the "number" or "label" of any
      Phone in the "phones" property of the card (as defined in
      Section 2.3.3 of [RFC9553]) matches the value.
   *

   *onlineService*: String
      A card matches this condition if the "service", "uri", "user", or
      "label" of any OnlineService in the "onlineServices" property of
      the card (as defined in Section 2.3.2 of [RFC9553]) matches the
      value.
   *

   *address*: String
      A card matches this condition if the value of any AddressComponent
      in the "addresses" property, property or the "full" property in the
      "addresses" property of the card, card (as defined in Section 2.5.1 of
      [RFC9553]) matches the value.
   *

   *note*: String
      A card matches this condition if the "note" of any Note in the
      "notes" property of the card (as defined in Section 2.8.3 of
      [RFC9553]) matches the value.

   If zero properties are specified on the FilterCondition, the
   condition MUST always evaluate to true.  If multiple properties are
   specified, ALL must apply for the condition to be true (it is
   equivalent to splitting the object into one-property conditions and
   making them all the child of an AND filter operator).

   The exact semantics for matching String fields is *deliberately deliberately not
   defined*
   defined to allow for flexibility in indexing implementation, subject
   to the following:

   *  Text SHOULD be matched in a case-insensitive manner.

   *  Text contained in either (but matched) single or double quotes
      SHOULD be treated as a *phrase search*, that is phrase search.  That is, a match is
      required for that exact sequence of words, excluding the
      surrounding quotation marks.  Use \", \' \', and \\ to match a
      literal ", ' ', and \ respectively in a phrase.

   *  Outside of a phrase, white-space whitespace SHOULD be treated as dividing
      separate tokens that may be searched for separately in the
      contact, but MUST all be present for the contact to match the
      filter.

   *  Tokens MAY be matched on a whole-word basis using stemming (so for
      example (e.g.,
      a text search for bus would match "buses" "buses", but not "business").

3.3.2.  Sorting

   The following value values for the "property" field on the Comparator
   object MUST be supported for sorting:

   *  "created" - The "created" date on the ContactCard.

   *  "updated" - The "updated" date on the ContactCard.

   The following values for the "property" field on the Comparator
   object SHOULD be supported for sorting:

   *  "name/given" - The value of the first NameComponent in the "name"
      property whose "kind" is "given".

   *  "name/surname" - The value of the first NameComponent in the
      "name" property whose "kind" is "surname".

   *  "name/surname2" - The value of the first NameComponent in the
      "name" property whose "kind" is "surname2".

3.4.  ContactCard/queryChanges

   This is a standard "/queryChanges" method as described in [RFC8620], Section 5.6. 5.6
   of [RFC8620].

3.5.  ContactCard/set

   This is a standard "/set" method as described in [RFC8620], Section 5.3. 5.3 of
   [RFC8620].

   To set a new photo, the file must first be uploaded using the upload
   mechanism as described in [RFC8620], Section 6.1. 6.1 of [RFC8620].  This will give
   the client a valid blobId/size/type blobId, size, and type to use.  The server MUST
   reject attempts to set a file that is not a recognised image type as
   the photo for a card.

3.6.  ContactCard/copy

   This is a standard "/copy" method as described in [RFC8620], Section 5.4. 5.4 of
   [RFC8620].

4.  Examples

   For brevity, in the following examples only the "methodCalls" property of the Request object, object
   and the "methodResponses" property of the Response object is shown. shown in
   the following examples.

4.1.  Fetching initial data Initial Data

   A user has authenticated and the client has fetched the JMAP Session
   object.  It finds a single Account with the
   "urn:ietf:params:jmap:contacts" capability, capability with id "a0x9", "a0x9" and wants
   to fetch all the address books and contacts.  It might make the
   following request:

   [
     ["AddressBook/get", {
       "accountId": "a0x9"
     }, "0"],
     ["ContactCard/get", {
       "accountId": "a0x9"
     }, "1"]
   ]

             Figure 1: "methodCalls" Property of a JMAP Request

   The server might respond with something like:

   [
     ["AddressBook/get", {
       "accountId": "a0x9",
       "list": [{
         "id": "062adcfa-105d-455c-bc60-6db68b69c3f3",
         "name": "Personal",
         "description": null,
         "sortOrder": 0,
         "isDefault": true,
         "isSubscribed": true,
         "shareWith": {
           "3f1502e0-63fe-4335-9ff3-e739c188f5dd": {
             "mayRead": true,
             "mayWrite": false,
             "mayShare": false,
             "mayDelete": false
           }
         },
         "myRights": {
           "mayRead": true,
           "mayWrite": true,
           "mayShare": true,
           "mayDelete": false
         }
       }, {
         "id": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe",
         "name": "Autosaved",
         "description": null,
         "sortOrder": 1,
         "isDefault": false,
         "isSubscribed": true,
         "shareWith": null,
         "myRights": {
           "mayRead": true,
           "mayWrite": true,
           "mayShare": true,
           "mayDelete": false
         }
       }],
       "notFound": [],
       "state": "~4144"
     }, "0"],
     ["Contact/get",
     ["ContactCard/get", {
       "accountId": "a0x9",
       "list": [{
         "id": "3",
         "addressBookIds": {
           "062adcfa-105d-455c-bc60-6db68b69c3f3": true
         },
         "name": {
           "components": [
             { "kind": "given", "value": "Joe" },
             { "kind": "surname", "value": "Bloggs" }
           ],
           "isOrdered": true
         },
         "emails": {
           "0": {
             "contexts": {
               "private": true
             },
             "address": "joe.bloggs@example.com"
           }
         }
       }],
       "notFound": [],
       "state": "ewarbckaqJ::112"
     }, "1"]
   ]

          Figure 2: "methodResponses" Property of a JMAP Response

4.2.  Changing the default address book Default Address Book

   The client tries to change the default address book from "Personal"
   to "Autosaved" (and makes no other change):

   [
     ["AddressBook/set", {
       "accountId": "a0x9",
       "onSuccessSetIsDefault": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe"
     }, "0"]
   ]

             Figure 3: "methodCalls" Property of a JMAP Request

   The server allows the change, returning the following response:

   [
     ["AddressBook/set", {
       "accountId": "a0x9",
       "updated": {
         "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe": {
           "isDefault": true
         },
         "062adcfa-105d-455c-bc60-6db68b69c3f3": {
           "isDefault": false
         }
         },
         "oldState": "~4144",
         "newState": "~4148"
       }
     }, "0"]
   ]

          Figure 4: "methodResponses" Property of a JMAP Response

5.  Internationalisation Considerations

   Experience has shown that unrestricted use of Unicode can lead to
   problems such as inconsistent rendering, users reading text and
   interpreting it differently than intended, and unexpected results
   when copying text from one location to another.  Servers MAY choose
   to mitigate this by restricting the set of characters allowed in
   otherwise unconstrained String fields.  The FreeformClass, as
   documented in [RFC8264], Section 4.3 of [RFC8264], might be a good starting
   point for this.

   Attempts to set a value containing code points outside of the
   permissible set can be handled in a few ways by the server.  The
   server could choose to strip the forbidden characters, characters or replace them
   with U+FFFD (the Unicode replacement character), character) and store the
   resulting string.  This is likely to be appropriate for non-printable
   characters  -- such as the "Control Codes" defined in [UNICODE] Section 23.1, 23.1
   (https://www.unicode.org/versions/latest/core-spec/chapter-
   23/#G20365) of [UNICODE], excluding newline (U+000A), carriage return
   (U+000D), and tab (U+0009) — which -- that can end up in data accidentally
   due to copy-
   and-paste issues, copy-and-paste issues but are invisible to the end user.  JMAP
   allows the server to transform data on create/update, create/update as long as any
   changed properties are returned to the client in the /set response, "/set" response
   so it knows what has changed, as per [RFC8620], Section 5.3. 5.3 of [RFC8620].
   Alternatively, the server MAY just reject the create/update with an
   invalidProperties
   "invalidProperties" SetError.

6.  Security Considerations

   All security considerations of JMAP ([RFC8620]) [RFC8620] apply to this
   specification.  Additional considerations specific to the data types
   and functionality introduced by this document are described in the
   following subsection.

   Contacts consist almost entirely of private, personally identifiable
   information, and represent the social connections of users.  Privacy
   leaks can have real world consequences, and contacts contact servers and
   clients MUST be mindful of the need to keep all data secure.

   Servers MUST enforce the ACLs Access Control Lists (ACLs) set on address
   books to ensure only authorised data is shared.

7.  IANA Considerations

7.1.  JMAP capability registration Capability Registration for "contacts"

   IANA will register has registered "contacts" in the "JMAP Capabilities" registry as
   follows:

   Capability Name:  urn:ietf:params:jmap:contacts

   Specification document: this document
   Intended use: Use:  common
   Change Controller:  IETF
   Security and privacy considerations: Privacy Considerations:  this document, Section 6
   Reference:  this document

7.2.  JMAP Data Type Registration for "AddressBook"

   IANA will register has registered "AddressBook" in the "JMAP Data Types" registry
   as follows:

   Type Name:  AddressBook
   Can reference blobs: no Reference Blobs:  No
   Can Use for State Change: yes  Yes
   Capability:  urn:ietf:params:jmap:contacts

   Specification document:
   Reference:  this document

7.3.  JMAP Data Type Registration for "ContactCard"

   IANA will register has registered "ContactCard" in the "JMAP Data Types" registry
   as follows:

   Type Name:  ContactCard
   Can reference blobs: yes Reference Blobs:  Yes
   Can Use for State Change: yes  Yes
   Capability:  urn:ietf:params:jmap:contacts

   Specification document:
   Reference:  this document

7.4.  JMAP Error Codes Registry

   The following subsection registers has registered a new error code in the "JMAP
   Error Codes" registry, as defined in Section 9 of [RFC8620].

7.4.1.  addressBookHasContents

   JMAP Error Code:  addressBookHasContents
   Intended use: Use:  common
   Change controller: Controller:  IETF

   Reference: This document, Section 2.3
   Description:  The AddressBook has at least one ContactCard assigned
      to it, and the "onDestroyRemoveContents" argument was false.
   Reference:  This document, Section 2.3

7.5.  JSContact Property Registrations

   IANA will register has registered the following additional properties in the
   JSContact Properties Registry,
   "JSContact Properties" registry, as defined in Section 3 of
   [RFC9553].

7.5.1.  id

   Property Name:  id
   Property Type: Not  not applicable
   Property Context:  Card
   Intended Use: Reserved Usage:  reserved
   Since Version:  1.0
   Change Controller:  IETF
   Reference:  this document

7.5.2.  addressBookIds

   Property Name:  addressBookIds
   Property Type: Not  not applicable
   Property Context:  Card
   Intended Use: Reserved Usage:  reserved
   Since Version:  1.0
   Change Controller:  IETF
   Reference:  this document

7.5.3.  blobId

   Property Name:  blobId
   Property Type: Not  not applicable
   Property Context:  Media
   Intended Use: Reserved Usage:  reserved
   Since Version:  1.0
   Change Controller:  IETF
   Reference:  this document

8.  References

8.1.  Normative References

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/info/rfc2119>.

   [RFC2397]  Masinter, L., "The "data" URL scheme", RFC 2397,
              DOI 10.17487/RFC2397, August 1998,
              <https://www.rfc-editor.org/info/rfc2397>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/info/rfc8174>.

   [RFC8620]  Jenkins, N. and C. Newman, "The JSON Meta Application
              Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July
              2019, <https://www.rfc-editor.org/info/rfc8620>.

   [RFC9553]  Stepanek, R. and M. Loffredo, "JSContact: A JSON
              Representation of Contact Data", RFC 9553,
              DOI 10.17487/RFC9553, May 2024,
              <https://www.rfc-editor.org/info/rfc9553>.

   [I-D.ietf-jmap-sharing]

   [RFC9670]  Jenkins, N., "JMAP Ed., "JSON Meta Application Protocol (JMAP)
              Sharing", Work in Progress, Internet-
              Draft, draft-ietf-jmap-sharing-09, 17 April RFC 9670, DOI 10.17487/RFC9670, November 2024,
              <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-
              sharing-09>.

9.
              <https://www.rfc-editor.org/info/rfc9670>.

8.2.  Informative References

   [RFC8264]  Saint-Andre, P. and M. Blanchet, "PRECIS Framework:
              Preparation, Enforcement, and Comparison of
              Internationalized Strings in Application Protocols",
              RFC 8264, DOI 10.17487/RFC8264, October 2017,
              <https://www.rfc-editor.org/info/rfc8264>.

   [UNICODE]  The Unicode Consortium, "The Unicode Standard",
              <http://www.unicode.org/versions/latest/>.  Note that this
              reference is to the latest version of Unicode, rather than
              to a specific release.  It is not expected that future
              changes in the Unicode Standard will affect the referenced
              definitions.
              <https://www.unicode.org/versions/latest/>.

Author's Address

   Neil Jenkins (editor)
   Fastmail
   PO Box 234, Collins St West
   Melbourne  VIC 8007
   Australia
   Email: neilj@fastmailteam.com
   URI:   https://www.fastmail.com