Skip to main content

JMAP Conditional Set
draft-ietf-jmap-conditional-00

Document Type Active Internet-Draft (jmap WG)
Author Bron Gondwana
Last updated 2026-09-15
Replaces draft-gondwana-jmap-conditional
RFC stream Internet Engineering Task Force (IETF)
Intended RFC status (None)
Formats
Additional resources Mailing list discussion
Stream WG state WG Document
Document shepherd (None)
IESG IESG state I-D Exists
Consensus boilerplate Unknown
Telechat date (None)
Responsible AD (None)
Send notices to (None)
draft-ietf-jmap-conditional-00
Network Working Group                                        B. Gondwana
Internet-Draft                                                  Fastmail
Updates: 8620 (if approved)                            15 September 2026
Intended status: Standards Track                                        
Expires: 19 March 2027

                          JMAP Conditional Set
                     draft-ietf-jmap-conditional-00

Abstract

   The JMAP base protocol ([JMAP-CORE]) provides the Foo/set method for
   creating, updating, and destroying objects.  It offers a single
   concurrency control, the "ifInState" argument, which guards an entire
   object type: if any object of that type has changed, the whole method
   is rejected.

   This extension adds a finer, per-object conditional mechanism.  A
   client may require that an individual update or destroy proceed only
   if the target object still matches a set of expected property values,
   expressed using the JMAP PatchObject already defined for updates.
   This provides optimistic concurrency control scoped to a single
   object — the equivalent of an HTTP "If-Match" precondition — for any
   JMAP data type.

   This extension also defines an optional "atomic" argument that
   applies an entire Foo/set as a single unit: either every change it
   requests takes effect, or none does.  Combined with the per-object
   precondition, this lets a client express a multi-object change that
   is safe only when applied together — such as an atomic rename that
   exchanges two names.

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 of the Internet Engineering
   Task Force (IETF).  Note that other groups may also distribute
   working documents as Internet-Drafts.  The list of current Internet-
   Drafts is at https://datatracker.ietf.org/drafts/current/.

   Internet-Drafts are draft documents valid for a maximum of six months
   and may be updated, replaced, or obsoleted by other documents at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

Gondwana                  Expires 19 March 2027                 [Page 1]
Internet-Draft              JMAP Conditional              September 2026

   This Internet-Draft will expire on 19 March 2027.

Copyright Notice

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

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents (https://trustee.ietf.org/
   license-info) in effect on the date of publication of this document.
   Please review these documents carefully, as they describe your rights
   and restrictions with respect to this document.  Code Components
   extracted from this document must include 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  . . . . . . . . . . . . . . . . . . . . . . . .   3
     1.1.  Conventions Used in This Document . . . . . . . . . . . .   4
   2.  Addition to the Capabilities Object . . . . . . . . . . . . .   4
     2.1.  urn:ietf:params:jmap:conditional  . . . . . . . . . . . .   4
       2.1.1.  Capability Example  . . . . . . . . . . . . . . . . .   4
   3.  Conditional Foo/set . . . . . . . . . . . . . . . . . . . . .   4
     3.1.  Evaluation Rules  . . . . . . . . . . . . . . . . . . . .   5
     3.2.  Effect  . . . . . . . . . . . . . . . . . . . . . . . . .   6
     3.3.  The "stateMismatch" error . . . . . . . . . . . . . . . .   6
   4.  Atomic Foo/set  . . . . . . . . . . . . . . . . . . . . . . .   7
     4.1.  Evaluation  . . . . . . . . . . . . . . . . . . . . . . .   7
     4.2.  Failure . . . . . . . . . . . . . . . . . . . . . . . . .   7
     4.3.  Servers That Cannot Apply Atomically  . . . . . . . . . .   8
     4.4.  Force-with-lease on file content  . . . . . . . . . . . .   8
     4.5.  Conditional destroy . . . . . . . . . . . . . . . . . . .   8
     4.6.  A condition that ignores unrelated changes  . . . . . . .   9
     4.7.  Conditioning on an object the method does not change  . .   9
     4.8.  Whole-object compare-and-swap . . . . . . . . . . . . . .  10
     4.9.  Atomic exchange of two names  . . . . . . . . . . . . . .  10
   5.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  10
     5.1.  JMAP Capability Registration for "conditional"  . . . . .  10
     5.2.  JMAP Error Code Registration for "atomicFailure"  . . . .  11
     5.3.  JMAP Error Code Registration for
           "cannotApplyAtomically" . . . . . . . . . . . . . . . . .  11
   6.  Security Considerations . . . . . . . . . . . . . . . . . . .  11
     6.1.  Information Disclosure  . . . . . . . . . . . . . . . . .  12
     6.2.  Atomic Methods and Resource Consumption . . . . . . . . .  12
     6.3.  Resource Consumption  . . . . . . . . . . . . . . . . . .  12
   7.  Changes . . . . . . . . . . . . . . . . . . . . . . . . . . .  12
   8.  References  . . . . . . . . . . . . . . . . . . . . . . . . .  13

Gondwana                  Expires 19 March 2027                 [Page 2]
Internet-Draft              JMAP Conditional              September 2026

     8.1.  Normative References  . . . . . . . . . . . . . . . . . .  13
     8.2.  Informative References  . . . . . . . . . . . . . . . . .  13
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . .  14

1.  Introduction

   The Foo/set method defined in [JMAP-CORE] is the mechanism by which
   clients create, update, and destroy objects.  It provides one form of
   concurrency control: the optional "ifInState" argument, which aborts
   the entire method if the account's state string for that data type
   does not match the value the client supplies.

   This guard is coarse.  The state string changes whenever _any_ object
   of that type changes, by any client.  In a busy account — or one that
   receives server-initiated changes, such as the arrival of new mail —
   the state can change between a client's read and its write for
   reasons entirely unrelated to the object the client is modifying.
   Using "ifInState" to protect a single update therefore leads to
   frequent spurious rejections and retries.

   Clients commonly need a narrower guarantee: "apply this change only
   if the specific object I read still holds the values I depend on".
   This is the same need met by the HTTP "If-Match" precondition
   ([HTTP-SEMANTICS]) and the entity-tag model of WebDAV ([WEBDAV]): a
   conditional write scoped to a single resource.

   This document defines a generic, per-object conditional mechanism for
   Foo/set.  It introduces no new properties and no new version tokens.
   Instead it reuses the PatchObject already defined by [JMAP-CORE] for
   updates: a client states its precondition as a PatchObject describing
   the values it expects the object to currently hold, and the server
   performs the change only if applying that patch would change nothing.

   Because the mechanism is defined entirely in terms of an object's
   properties and the existing PatchObject semantics, it applies
   uniformly to every data type that implements Foo/set, with no per-
   type additions.

   A second, related need is atomicity.  [JMAP-CORE] processes the
   creations, updates, and destroys within a Foo/set independently: some
   may succeed while others fail, and the method gives no guarantee that
   a set of related changes takes effect together.  The only all-or-
   nothing control Core offers is "ifInState", which — like the
   concurrency problem above — is scoped to the entire object type
   rather than to the changes the client actually requested.  This
   document therefore also defines an optional "atomic" argument that
   commits a Foo/set as a single unit, evaluated against the state that
   results from applying all of its changes together (see Section 4).

Gondwana                  Expires 19 March 2027                 [Page 3]
Internet-Draft              JMAP Conditional              September 2026

1.1.  Conventions Used in This Document

   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.

   The terms "Id", "PatchObject", "SetError", and "Foo/set" are defined
   in [JMAP-CORE].  A "pointer" is a JSON Pointer as used within a
   PatchObject (see [JMAP-CORE], Section 5.3).

2.  Addition to the Capabilities Object

2.1.  urn:ietf:params:jmap:conditional

   The presence of the "urn:ietf:params:jmap:conditional" property in
   the "capabilities" object of the JMAP Session resource indicates
   support for the conditional Foo/set behaviour defined in this
   document.  The value of this property MUST be an empty object.

   A client indicates that it wishes to use this extension by including
   the capability URI in the "using" array of a request.  When a server
   that advertises this capability receives a request whose "using"
   array includes this URI, it MUST honour the "ifUnchangedBy" argument
   (Section 3) on every Foo/set method it implements.  It MUST also
   honour the "atomic" argument (Section 4): either applying the method
   atomically, or, if it cannot, rejecting it with a
   "cannotApplyAtomically" error (Section 4).  A server MUST NOT apply
   the changes of a method requested with "atomic" only in part.

   The capability is generic: it does not depend on any particular data
   type, and its presence applies to all types for which the server
   implements Foo/set.

2.1.1.  Capability Example

   "urn:ietf:params:jmap:conditional": {}

3.  Conditional Foo/set

   This document adds the following arguments to the standard Foo/set
   method ([JMAP-CORE], Section 5.3):

   *  ifUnchangedBy: "Id[PatchObject]" (default: an empty object)

Gondwana                  Expires 19 March 2027                 [Page 4]
Internet-Draft              JMAP Conditional              September 2026

      A map of object id to a PatchObject expressing a precondition on
      that object.  For each entry, the precondition is satisfied if and
      only if applying the given PatchObject to the current server-side
      object would leave the object unchanged.

      Equivalently: for every pointer in the PatchObject, the object's
      current value at that pointer MUST equal the value given in the
      PatchObject.  A JSON "null" value matches a property that is not
      present (it asserts that removing the property would be a no-op).
      Comparison uses the same representation the server would return
      for that property from Foo/get.

   *  atomic: "Boolean" (default: false)

      If true, the Foo/set is applied as a single unit: the server MUST
      either commit every creation, update, and destroy the method
      requests, or make no change at all.  See Section 4.

3.1.  Evaluation Rules

   1.  Preconditions are evaluated against the state of each object as
       it exists at the start of the method, before any create, update,
       or destroy in the same method is applied.

   2.  Each key in "ifUnchangedBy" is the id of an object of the same
       type as the method (the "Foo" of "Foo/set").  The object need not
       be one the method changes.  A client MAY set a precondition on an
       object the method does not change, for example its parent.  A
       precondition is not satisfied if its object does not exist, or is
       not visible to the client.  The server then handles it as in
       Section 3.2.  A creation id (an id prefixed with "#") MAY be a
       key.  It references an object created by an earlier method call
       in the same request ([JMAP-CORE], Section 5.3).  If the creation
       id resolves to an object of a different type, the server MUST
       reject the whole method with an "invalidArguments" error.  This
       lets a client require that an object it created earlier in the
       request has not changed since then.

   3.  Unlike the PatchObject supplied in the "update" argument, which
       may only reference properties the client is permitted to set, the
       PatchObject in "ifUnchangedBy" MAY reference any property of the
       object, including server-set and otherwise read-only properties
       (for example a content identifier, a size, or a server-maintained
       change timestamp).  This allows a client to condition a change on
       properties it cannot itself modify.

Gondwana                  Expires 19 March 2027                 [Page 5]
Internet-Draft              JMAP Conditional              September 2026

   4.  If a pointer in an "ifUnchangedBy" PatchObject is not a valid
       pointer for the object's type, or the PatchObject is otherwise
       malformed, the server MUST reject the whole method with an
       "invalidArguments" error.

   5.  The "ifUnchangedBy" and "ifInState" arguments are both method-
       level guards and compose.  If both are supplied, the server
       checks "ifInState" first and then the "ifUnchangedBy"
       preconditions; if either does not hold, the method is rejected
       with a "stateMismatch" error and no change is made.

3.2.  Effect

   If every precondition in "ifUnchangedBy" is satisfied, the method
   proceeds exactly as it would without this extension.

   If any precondition is not satisfied, the server MUST make no change
   — no creation, update, or destroy in the method takes effect — and
   MUST reject the whole method with a method-level "stateMismatch"
   error (Section 3.3).  The state string for the type MUST be
   unchanged.

   This is all-or-nothing at the method level, by design: a single Foo/
   set is the unit of conditional application.  A client that wants
   changes to succeed or fail independently of one another places them
   in separate method calls within the same request, each with its own
   "ifUnchangedBy".

3.3.  The "stateMismatch" error

   A failed "ifUnchangedBy" precondition is reported using the method-
   level "stateMismatch" error already defined by [JMAP-CORE] for a
   failed "ifInState" guard.  It is returned as an "error" response in
   place of the Foo/set response, so no create, update, or destroy
   result is returned and the client knows the method made no change.

   The error discloses no object data: a client that needs the current
   values fetches them with Foo/get or Foo/changes before deciding how
   to proceed.  A server MAY include a "description" for debugging, and
   MAY include:

   *  failed: "Id[]"

      The ids of the objects whose preconditions were not satisfied.
      This lets a client re-fetch only what it needs, without disclosing
      any values.  A server that does not wish to distinguish which
      precondition failed MAY omit this property.

Gondwana                  Expires 19 March 2027                 [Page 6]
Internet-Draft              JMAP Conditional              September 2026

4.  Atomic Foo/set

   When the "atomic" argument is true, the server MUST apply the Foo/set
   as a single unit: either every creation, update, and destroy the
   method requests takes effect, or none of them does.

4.1.  Evaluation

   The server evaluates the method as though all of its changes were
   applied together.  Any constraint that [JMAP-CORE] or a data-type
   extension evaluates on the resulting object set — for example a
   uniqueness constraint on a name within a parent — is checked once,
   against the state produced by applying all the changes, not against
   any intermediate state.

   This is what lets a set of changes that is valid only as a whole
   succeed.  For example, exchanging two names — renaming object A to
   the name currently held by B and B to the name currently held by A —
   collides if applied one change at a time, but the combined result is
   conflict-free (see Section 4.9).

4.2.  Failure

   If any part of an atomic method fails — an invalid patch, a name
   collision, a permission error, a violated constraint, or any other
   SetError that would place an id in "notCreated", "notUpdated", or
   "notDestroyed" — the server MUST make no change and MUST reject the
   whole method with a method-level "atomicFailure" error.  The state
   string for the type MUST be unchanged.

   A failed "ifUnchangedBy" precondition is reported as a
   "stateMismatch" error (Section 3.3) whether or not "atomic" is set;
   preconditions are already all-or-nothing at the method level.  The
   "atomicFailure" error is specific to the method's own creations,
   updates, and destroys not being committable together.

   The "atomicFailure" error object MAY carry "notCreated",
   "notUpdated", and "notDestroyed" maps — of the same form as a Foo/set
   response — identifying the ids whose processing failed and the
   SetError for each.  Ids that would have succeeded are omitted; they
   were not committed, because the method as a whole was rejected.  A
   server MAY report only the first failure it encounters.

Gondwana                  Expires 19 March 2027                 [Page 7]
Internet-Draft              JMAP Conditional              September 2026

4.3.  Servers That Cannot Apply Atomically

   A server that advertises this capability but cannot apply a given
   method atomically — either at all, or because this particular method
   is beyond what it can commit as a unit — MUST reject the method with
   a method-level "cannotApplyAtomically" error and make no change.  It
   MUST NOT fall back to applying the changes non-atomically.

   A client that receives "cannotApplyAtomically" MAY retry without
   "atomic" if it can tolerate partial application, or surface the
   failure otherwise.  A client that does not recognise the error treats
   it as "serverFail" ([JMAP-CORE]): since a rejected atomic method
   makes no change, this fallback is safe, if less informative.

4.4.  Force-with-lease on file content

   A client last synchronised a file node "f42" with content blob
   "G_old".  It has since produced new content "G_new", and wishes to
   replace the content only if no other client has changed it in the
   meantime.  It conditions on the server-set "blobId"
   ([JMAP-FILENODE]):

   [[ "FileNode/set", {
     "accountId": "u1",
     "ifUnchangedBy": { "f42": { "blobId": "G_old" } },
     "update":       { "f42": { "blobId": "G_new",
                                "modified": "2026-05-01T09:30:00Z" } }
   }, "0" ]]

   If "f42" still references "G_old", the update succeeds.  If another
   client has already replaced the content (so "blobId" is now, say,
   "G_other"), the precondition fails and the server makes no change,
   returning a method-level "stateMismatch" error:

   [[ "error", {
     "type": "stateMismatch",
     "failed": [ "f42" ]
   }, "0" ]]

   The client fetches "f42", resolves the conflict (for instance by
   preserving its own version as a separate file), and retries.

4.5.  Conditional destroy

   Destroy a message only if it has not been read on another device.  In
   [JMAP-MAIL], an unread message has no "$seen" keyword, so the client
   asserts that "keywords/$seen" is absent:

Gondwana                  Expires 19 March 2027                 [Page 8]
Internet-Draft              JMAP Conditional              September 2026

   [[ "Email/set", {
     "accountId": "u1",
     "ifUnchangedBy": { "M7": { "keywords/$seen": null } },
     "destroy":      [ "M7" ]
   }, "0" ]]

   If the message has since been marked "$seen", the precondition fails,
   the whole method is rejected with a "stateMismatch" error, and "M7"
   is not deleted.

4.6.  A condition that ignores unrelated changes

   Because the precondition references only the pointers the client
   supplies, concurrent changes to other properties of the same object
   do not cause it to fail.  Here a client removes a grantee's access,
   but only if that grantee currently holds exactly the rights the
   client saw:

   [[ "FileNode/set", {
     "accountId": "u1",
     "ifUnchangedBy": { "d3": { "shareWith/bob/mayRead": true } },
     "update":       { "d3": { "shareWith/bob": null } }
   }, "0" ]]

   The change applies only if Bob currently has "mayRead" of "true".  A
   concurrent change to an unrelated property of "d3" (its name, or
   another grantee's rights) does not trigger a "stateMismatch".

4.7.  Conditioning on an object the method does not change

   A precondition may name an object the method is not creating,
   updating, or destroying.  Here a client updates the content of file
   "f42", but only if it is still in the parent directory the client
   last saw ("d3"), so the write does not land in an unexpected place
   after a concurrent move:

   [[ "FileNode/set", {
     "accountId": "u1",
     "ifUnchangedBy": { "f42": { "parentId": "d3" },
                        "d3":  { "name": "Reports" } },
     "update":       { "f42": { "blobId": "G_new",
                                "modified": "2026-05-01T09:30:00Z" } }
   }, "0" ]]

   If either "f42" has been reparented or "d3" has been renamed, the
   method is rejected with a "stateMismatch" error and the content is
   not written, even though "d3" itself is not being changed.

Gondwana                  Expires 19 March 2027                 [Page 9]
Internet-Draft              JMAP Conditional              September 2026

4.8.  Whole-object compare-and-swap

   A data type that maintains a server-set change token can be used to
   require that nothing at all has changed.  For example, a type with a
   server-maintained "changed" timestamp that is updated on every
   modification:

   "ifUnchangedBy": { "f42": { "changed": "2026-04-30T12:00:00Z" } }

   Because the server updates "changed" on any modification to the
   object, this asserts that the entire object is unchanged since the
   client last read it, recovering the coarse "If-Match" behaviour as a
   special case of the general mechanism.

4.9.  Atomic exchange of two names

   Two file nodes, "a" (named "current.txt") and "b" (named
   "previous.txt"), are to swap names.  Applied one at a time, either
   rename collides with the name the other still holds; applied
   together, the result is conflict-free.  With "atomic" set, the rule
   that sibling names are unique is evaluated only against the resulting
   state:

   [[ "FileNode/set", {
     "accountId": "u1",
     "atomic": true,
     "update": {
       "a": { "name": "previous.txt" },
       "b": { "name": "current.txt" }
     }
   }, "0" ]]

   Both renames take effect, or — if either is rejected for any reason —
   neither does, and the server returns an "atomicFailure" error
   identifying the offending id.

5.  IANA Considerations

5.1.  JMAP Capability Registration for "conditional"

   IANA is requested to register the "conditional" JMAP Capability as
   follows, in the "JMAP Capabilities" registry established by
   [JMAP-CORE]:

   Capability Name:  urn:ietf:params:jmap:conditional

   Specification document:  this document

Gondwana                  Expires 19 March 2027                [Page 10]
Internet-Draft              JMAP Conditional              September 2026

   Intended use:  common

   Change Controller:  IETF

   Security and privacy considerations:  this document, Section 6

5.2.  JMAP Error Code Registration for "atomicFailure"

   IANA is requested to register the "atomicFailure" JMAP Error Code in
   the "JMAP Error Codes" registry established by [JMAP-CORE]:

   JMAP Error Code:  atomicFailure

   Intended use:  common

   Change Controller:  IETF

   Description:  A Foo/set requested with "atomic" set to true could not
      commit all of its changes together; no change was made.  Returned
      as a method-level error.

   Reference:  this document

5.3.  JMAP Error Code Registration for "cannotApplyAtomically"

   IANA is requested to register the "cannotApplyAtomically" JMAP Error
   Code in the "JMAP Error Codes" registry established by [JMAP-CORE]:

   JMAP Error Code:  cannotApplyAtomically

   Intended use:  common

   Change Controller:  IETF

   Description:  A Foo/set was requested with "atomic" set to true, but
      the server cannot apply this method atomically; no change was
      made.  Returned as a method-level error.

   Reference:  this document

6.  Security Considerations

Gondwana                  Expires 19 March 2027                [Page 11]
Internet-Draft              JMAP Conditional              September 2026

6.1.  Information Disclosure

   The conditional mechanism returns no object data.  A failed
   precondition yields only a "stateMismatch" error, optionally naming
   the ids whose preconditions did not hold, but never their values —
   nothing beyond what the client could already obtain by reading the
   objects with Foo/get.

   A precondition does not bypass access control.  A server MUST require
   the same permissions to read the properties referenced in an
   "ifUnchangedBy" PatchObject as it would to return them from Foo/get.
   In particular, a server MUST NOT evaluate a precondition on a
   property the requesting client is not permitted to read; such a
   condition MUST cause the method to fail with a "forbidden" error, so
   that the mechanism cannot be used as an oracle to probe the values of
   properties the client cannot otherwise see.

6.2.  Atomic Methods and Resource Consumption

   Applying a method atomically may require the server to hold a
   transaction, or equivalent locks, across all of its changes, and so a
   very large atomic Foo/set can hold resources longer than the same
   changes made independently.  The limits a server already applies to
   the number of objects in a single Foo/set ([JMAP-CORE]) continue to
   apply, and a server MAY reject an atomic method it considers too
   large rather than attempt it.

6.3.  Resource Consumption

   Evaluating a precondition is comparable in cost to validating an
   update PatchObject and imposes no significant additional load.

7.  Changes

   EDITOR: please remove this section before publication.

   The source of this document exists on github at:
   https://github.com/brong/draft-gondwana-jmap-conditional/

   *draft-ietf-jmap-conditional-00*

   *  Upload with working-group adopted name.

   *draft-gondwana-jmap-conditional-01*

Gondwana                  Expires 19 March 2027                [Page 12]
Internet-Draft              JMAP Conditional              September 2026

   *  Added the "atomic" argument to Foo/set: a method requested with
      "atomic" is applied as a single unit, with constraints evaluated
      against the resulting state (enabling changes valid only as a
      whole, such as an atomic name exchange).

   *  Added the "atomicFailure" method-level error (the method's changes
      could not be committed together) and the "cannotApplyAtomically"
      method-level error (the server cannot apply the method
      atomically), both registered with IANA.  A server MUST NOT apply
      an atomic method's changes only in part.

   *  Generalised "ifUnchangedBy" to allow a precondition on any object
      of the method's type, including objects the method does not itself
      create, update, or destroy (for example a parent or container).

   *  Changed the effect of a failed precondition from a per-object
      "stateMismatch" SetError to a method-level "stateMismatch" error
      that rejects the whole method and makes no change; a client
      wanting independent application uses separate method calls.
      Removed the bespoke per-object SetError definition in favour of
      Core's method-level "stateMismatch", optionally carrying the
      "failed" ids.

   *draft-gondwana-jmap-conditional-00*

   *  Initial proposal.

8.  References

8.1.  Normative References

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

   [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/rfc/rfc2119>.

   [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/rfc/rfc8174>.

8.2.  Informative References

Gondwana                  Expires 19 March 2027                [Page 13]
Internet-Draft              JMAP Conditional              September 2026

   [HTTP-SEMANTICS]
              Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
              Ed., "HTTP Semantics", STD 97, RFC 9110,
              DOI 10.17487/RFC9110, June 2022,
              <https://www.rfc-editor.org/rfc/rfc9110>.

   [JMAP-FILENODE]
              Gondwana, B., "JMAP File Storage extension", Work in
              Progress, Internet-Draft, draft-ietf-jmap-filenode-14, 15
              May 2026, <https://datatracker.ietf.org/doc/html/draft-
              ietf-jmap-filenode-14>.

   [JMAP-MAIL]
              Jenkins, N. and C. Newman, "The JSON Meta Application
              Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621,
              August 2019, <https://www.rfc-editor.org/rfc/rfc8621>.

   [WEBDAV]   Dusseault, L., Ed., "HTTP Extensions for Web Distributed
              Authoring and Versioning (WebDAV)", RFC 4918,
              DOI 10.17487/RFC4918, June 2007,
              <https://www.rfc-editor.org/rfc/rfc4918>.

Author's Address

   Bron Gondwana
   Fastmail
   Email: brong@fastmailteam.com

Gondwana                  Expires 19 March 2027                [Page 14]