Skip to main content

JMAP Object History
draft-ietf-jmap-object-history-02

Document Type Active Internet-Draft (jmap WG)
Author Bron Gondwana
Last updated 2026-10-16
Replaces draft-gondwana-jmap-object-history
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-object-history-02
Network Working Group                                        B. Gondwana
Internet-Draft                                                  Fastmail
Intended status: Standards Track                         16 October 2026
Expires: 19 April 2027

                          JMAP Object History
                   draft-ietf-jmap-object-history-02

Abstract

   The JMAP base protocol (RFC8620) provides methods for synchronizing
   the current state of data objects between client and server.  This
   extension adds the ability to retrieve historical versions of
   objects, including objects that have been destroyed, by extending the
   standard Foo/get method.

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."

   This Internet-Draft will expire on 19 April 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.

Gondwana                  Expires 19 April 2027                 [Page 1]
Internet-Draft             JMAP Object History              October 2026

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   2
     1.1.  Conventions Used in This Document . . . . . . . . . . . .   3
   2.  Addition to the Capabilities Object . . . . . . . . . . . . .   3
     2.1.  urn:ietf:params:jmap:object-history . . . . . . . . . . .   3
   3.  Extensions to Foo/get . . . . . . . . . . . . . . . . . . . .   4
     3.1.  The objectHistory property  . . . . . . . . . . . . . . .   6
     3.2.  Examples  . . . . . . . . . . . . . . . . . . . . . . . .   7
       3.2.1.  Contact history . . . . . . . . . . . . . . . . . . .   8
       3.2.2.  Recovering destroyed emails . . . . . . . . . . . . .  10
   4.  Restoring Objects . . . . . . . . . . . . . . . . . . . . . .  11
   5.  Design Rationale  . . . . . . . . . . . . . . . . . . . . . .  12
   6.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  12
     6.1.  JMAP Capability Registration for
           urn:ietf:params:jmap:object-history . . . . . . . . . . .  13
     6.2.  Update to the JMAP Data Types Registry  . . . . . . . . .  13
     6.3.  JSContact Property Registration . . . . . . . . . . . . .  15
     6.4.  JSCalendar Property Registration  . . . . . . . . . . . .  15
   7.  Security Considerations . . . . . . . . . . . . . . . . . . .  16
     7.1.  Storage and Resource Consumption  . . . . . . . . . . . .  16
     7.2.  Access Control  . . . . . . . . . . . . . . . . . . . . .  16
     7.3.  Information Disclosure  . . . . . . . . . . . . . . . . .  16
     7.4.  Privacy Considerations  . . . . . . . . . . . . . . . . .  17
   8.  Changes . . . . . . . . . . . . . . . . . . . . . . . . . . .  17
   9.  Acknowledgements  . . . . . . . . . . . . . . . . . . . . . .  19
   10. References  . . . . . . . . . . . . . . . . . . . . . . . . .  19
     10.1.  Normative References . . . . . . . . . . . . . . . . . .  19
     10.2.  Informative References . . . . . . . . . . . . . . . . .  19
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . .  20

1.  Introduction

   JMAP ([JMAP-CORE] — JSON Meta Application Protocol) provides a
   standard set of methods for each data type: Foo/get, Foo/changes,
   Foo/set, Foo/query, and Foo/queryChanges.  These methods operate on
   the current state of objects.  The Foo/changes method tells a client
   which objects have changed, but does not provide the previous values.

   In many applications it is useful to access historical versions of
   objects.  For example, a user may want to see previous versions of a
   contact record ([JMAP-CONTACTS]) or recover a deleted email
   ([JMAP-MAIL]).

Gondwana                  Expires 19 April 2027                 [Page 2]
Internet-Draft             JMAP Object History              October 2026

   This extension adds history support to the standard Foo/get method.
   When the urn:ietf:params:jmap:object-history capability is included
   in the request's using array, Foo/get gains additional parameters
   that allow the client to request previous versions of objects,
   including objects that have been destroyed.

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.

2.  Addition to the Capabilities Object

   The capabilities object is returned as part of the JMAP Session
   object; see [JMAP-CORE], Section 2.

   This document defines an additional capability URI.

2.1.  urn:ietf:params:jmap:object-history

   The capability urn:ietf:params:jmap:object-history being present in
   the "accountCapabilities" property of an account represents support
   for retrieving historical object versions in that account.  Servers
   that include the capability in one or more "accountCapabilities"
   properties MUST also include the property in the "capabilities"
   property.

   The value of this property in the JMAP session "capabilities"
   property MUST be an empty object.

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

   *  maxHistoryDuration: "UnsignedInt|null"

      The number of seconds after replacement for which the server
      intends to retain old versions, or null if the server does not
      have a time-based retention policy.  This is a statement of server
      policy, not a guarantee: retention is best effort, and the server
      MAY discard versions sooner (for example, due to storage limits).
      A version whose replaced timestamp is more than this many seconds
      ago has probably been discarded.  Clients that need to be certain
      an object remains available should not destroy it.

Gondwana                  Expires 19 April 2027                 [Page 3]
Internet-Draft             JMAP Object History              October 2026

   *  supportedTypes: "String[]"

      The names of the data types, from the "JMAP Data Types" registry
      [IANA-JMAP] or private types, for which the server supports the
      history extensions in this account.  A server MAY support history
      for only a subset of the types it supports, and MAY retain history
      for only some objects of a supported type (for example, only for
      some folders).  Clients MUST ignore type names they do not
      recognise.

3.  Extensions to Foo/get

   When this capability is included in the request's using array, the
   standard Foo/get method ([JMAP-CORE], Section 5.1) accepts the
   following additional request arguments:

   *  includeReplaced: "Boolean" (default: false)

      If true, the server returns previous versions of the requested
      objects in addition to the current version.  Each previous version
      appears as a separate entry in the list array, with the same id as
      the current version.  Every entry includes an objectHistory
      property (see below).  Entries with the same id MUST be ordered by
      objectHistory.version (ascending).  Entries for different objects
      MAY be interleaved.

   *  includeDestroyed: "Boolean" (default: false)

      If true, the server also returns objects that have been destroyed.
      If includeReplaced is false, only the final version before
      destruction is returned.  If includeReplaced is true, all retained
      historical versions of destroyed objects are returned.  For
      destroyed objects, every entry has a non-null
      objectHistory.replaced value, since every version has been
      superseded (the final version was replaced by destruction).

   *  historyAfter: "UTCDate|null" (default: null)

      If supplied, only versions with an objectHistory.replaced value
      strictly after this date are returned, plus the current (live)
      version which has a null replaced value.  If null, no time-based
      filter is applied.

   *  historyBefore: "UTCDate|null" (default: null)

Gondwana                  Expires 19 April 2027                 [Page 4]
Internet-Draft             JMAP Object History              October 2026

      If supplied, only versions with an objectHistory.replaced value
      strictly before this date are returned.  The current (live)
      version, which has a null replaced value, is not returned.  If
      null, no time-based filter is applied.

      A client can use this to page backwards through history: it passes
      the oldest replaced value from the previous response as
      historyBefore in the next request.  When several ids are
      requested, the client passes the oldest replaced value from the
      whole response; any id with no remaining versions is returned in
      notFound.  Clients SHOULD pass the replaced value exactly as
      received, rather than parsing and re-serialising it, since re-
      serialising at a lower precision could cause versions to be
      skipped.

   *  historySkip: "UnsignedInt" (default: 0)

      The number of entries to skip for each id, counting from the most
      recent (the highest version number), before any entries are
      returned.  The current version, if it would otherwise be returned,
      counts as one entry.  For example, a client that wants only
      versions older than the 20 most recent passes historySkip: 20; any
      id with 20 or fewer matching entries is returned in notFound.

      A client can also use this to page backwards through history, by
      adding the number of entries returned for an id to historySkip in
      the next request.  This is less robust than historyBefore: if a
      new version is recorded between requests, the window shifts and
      one entry is returned again.

   *  historyLimit: "UnsignedInt|null" (default: null)

      If supplied, the maximum number of entries to return in the list
      array for each id, including the current version if any.  For each
      id, the server MUST return the most recent entries (those with the
      highest version numbers) remaining after historySkip is applied,
      not the oldest.  If null, the server returns all remaining
      retained versions, subject to its own limits.

   The server applies includeReplaced, includeDestroyed, historyAfter,
   and historyBefore to select the matching versions of each id, then
   skips historySkip entries, then returns at most historyLimit of the
   remaining entries.

   If the ids argument is null, the server returns all objects of the
   type as per [JMAP-CORE], Section 5.1.  If includeDestroyed is true,
   this includes every destroyed object for which the server still
   retains any version.

Gondwana                  Expires 19 April 2027                 [Page 5]
Internet-Draft             JMAP Object History              October 2026

   The "maxObjectsInGet" limit and the "requestTooLarge" error
   ([JMAP-CORE], Section 5.1) apply to the number of distinct ids, not
   to the number of entries in the list array.  Each id can have up to
   historyLimit entries.

   If, after applying includeReplaced, includeDestroyed, historyAfter,
   historyBefore, and historySkip, no versions of a requested id remain
   to be returned, that id is included in the notFound array.

3.1.  The objectHistory property

   When includeReplaced or includeDestroyed is true, each object in the
   list array includes an additional property:

   *  objectHistory: "ObjectHistoryInfo"

      An ObjectHistoryInfo object with the following properties:

      -  version: "UnsignedInt" A per-object monotonically increasing
         number that provides ordering among the versions of a single
         object within a response.  Version numbers MAY have gaps (e.g.,
         a server may use internal sequence numbers such as modseq
         values).  The current (live) version of an object always has
         the highest version number.  Servers SHOULD return consistent
         version numbers across requests, but clients MUST NOT rely on
         this — version numbers exist primarily to provide ordering
         within a single response.

      -  replaced: "UTCDate|null" The date and time at which this
         version was superseded, either by a subsequent update or by
         destruction.  If null, this is the current live version of the
         object.  For destroyed objects, every version has a non-null
         replaced value — the final version's replaced is the time of
         destruction.  The replaced values of different versions of the
         same object MUST be distinct, so that historyBefore and
         historyAfter partition the history without ambiguity; a server
         that records two versions with the same timestamp can collapse
         them into a single version.  The value MAY include fractional
         seconds ([JMAP-CORE], Section 1.4), but servers SHOULD NOT use
         more than millisecond precision, as many client date libraries
         cannot represent more.

      -  hasOlder: "Boolean" If true, the server retains at least one
         older version of this object than this one, whether or not that
         version was returned in this response.  A client can check
         hasOlder on the oldest entry returned for an id to learn
         whether more history is available, and retrieve it using
         historyBefore or historySkip.  The server MAY return false even

Gondwana                  Expires 19 April 2027                 [Page 6]
Internet-Draft             JMAP Object History              October 2026

         if older versions exist (e.g., if it cannot efficiently
         determine whether older versions remain).  If the server
         returns true, the client MUST NOT assume the older versions
         will still be available in a subsequent request, as the server
         may purge history at any time.

   When includeReplaced and includeDestroyed are both false (the
   default), the objectHistory property is not included in the response,
   and the behaviour is identical to standard Foo/get.

   The client MAY include "objectHistory" in the properties argument.
   It is returned if and only if includeReplaced or includeDestroyed is
   true, regardless of whether it is listed in properties.

   Which changes produce a version is server dependent: a version is a
   snapshot of the object that the server recorded at some point.  The
   server is not required to create a new version for every individual
   change to an object.  For example, rapid successive updates may be
   collapsed into a single version, and a server may choose not to
   record a version for changes that only affect server-computed
   properties.  The server MAY also prune old versions in any order, so
   there can be gaps in the version history available to the client.
   Clients MUST NOT assume that the version history is complete or
   contiguous.

   The values of server-computed properties (for example, the counts on
   a Mailbox) in a historical version are also server dependent.  The
   server SHOULD return the value the property had at the time of that
   version if it has that information.

   If a historical version contains a reference to a blob (for example,
   the blobId of an Email), that blob MUST be downloadable for as long
   as the server returns that version.  A server that has discarded a
   blob referenced by a version MUST NOT return that version.

   If includeReplaced or includeDestroyed is true and the data type is
   not listed in the supportedTypes capability property, the server MUST
   reject the call with an "invalidArguments" error.

   If the data type is supported but the server does not retain history
   for a particular object, it MUST still return the current version
   with an objectHistory property when requested.  In this case, the
   server returns only the current version, with a replaced of null.

3.2.  Examples

Gondwana                  Expires 19 April 2027                 [Page 7]
Internet-Draft             JMAP Object History              October 2026

3.2.1.  Contact history

   Requesting the history of a contact, including all versions:

   [["ContactCard/get", {
     "accountId": "abc",
     "ids": ["contact1"],
     "properties": ["name", "emails"],
     "includeReplaced": true,
     "historyLimit": 10
   }, "0"]]

   The response includes three versions: the original creation, a name
   update, and the current version with an additional email address:

Gondwana                  Expires 19 April 2027                 [Page 8]
Internet-Draft             JMAP Object History              October 2026

   [["ContactCard/get", {
     "accountId": "abc",
     "state": "abc123",
     "list": [
       {
         "id": "contact1",
         "name": {"full": "Robert Smith"},
         "emails": {
           "e1": {"address": "bob@example.com"}
         },
         "objectHistory": {
           "version": 1,
           "replaced": "2026-02-20T14:15:00Z",
           "hasOlder": false
         }
       },
       {
         "id": "contact1",
         "name": {"full": "Bob Smith"},
         "emails": {
           "e1": {"address": "bob@example.com"}
         },
         "objectHistory": {
           "version": 2,
           "replaced": "2026-03-01T09:00:00Z",
           "hasOlder": true
         }
       },
       {
         "id": "contact1",
         "name": {"full": "Bob Smith"},
         "emails": {
           "e1": {"address": "bob@example.com"},
           "e2": {"address": "bob@personal.example"}
         },
         "objectHistory": {
           "version": 3,
           "replaced": null,
           "hasOlder": true
         }
       }
     ],
     "notFound": []
   }, "0"]]

Gondwana                  Expires 19 April 2027                 [Page 9]
Internet-Draft             JMAP Object History              October 2026

3.2.2.  Recovering destroyed emails

   A client can use Email/changes to discover which emails have been
   destroyed, then use a backreference to fetch their final state.  This
   is useful for displaying a "recently deleted" view:

   [
     ["Email/changes", {
       "accountId": "abc",
       "sinceState": "state42"
     }, "0"],
     ["Email/get", {
       "accountId": "abc",
       "#ids": {
         "resultOf": "0",
         "name": "Email/changes",
         "path": "/destroyed"
       },
       "properties": ["subject", "from", "receivedAt"],
       "includeDestroyed": true
     }, "1"]
   ]

   The Email/get response returns the final state of each destroyed
   email.  The objectHistory.replaced value is the time of destruction:

Gondwana                  Expires 19 April 2027                [Page 10]
Internet-Draft             JMAP Object History              October 2026

   [["Email/get", {
     "accountId": "abc",
     "state": "state43",
     "list": [
       {
         "id": "email42",
         "subject": "Meeting notes",
         "from": [{"name": "Alice",
           "email": "alice@example.com"}],
         "receivedAt": "2026-03-10T09:00:00Z",
         "objectHistory": {
           "version": 1,
           "replaced": "2026-03-15T16:30:00Z",
           "hasOlder": false
         }
       },
       {
         "id": "email57",
         "subject": "Project update",
         "from": [{"name": "Bob",
           "email": "bob@example.com"}],
         "receivedAt": "2026-03-12T14:00:00Z",
         "objectHistory": {
           "version": 1,
           "replaced": "2026-03-15T16:31:00Z",
           "hasOlder": false
         }
       }
     ],
     "notFound": []
   }, "1"]]

   Since includeReplaced was not set, only the final version of each
   destroyed email is returned.  The non-null replaced value indicates
   when each email was destroyed.

4.  Restoring Objects

   To restore an earlier version of an existing object, the client uses
   a normal Foo/set update with the values from that version.

   To restore a destroyed object, the client uses Foo/set to create a
   new object with the values from the version to be restored.  When
   this capability is included in the request's using array, the client
   MAY include the id of the destroyed object in the create request.
   The server MAY honour this, in which case the created object has that
   id and its history continues from the destroyed object's history, so
   the destroyed versions become previous versions of the restored

Gondwana                  Expires 19 April 2027                [Page 11]
Internet-Draft             JMAP Object History              October 2026

   object.  If the server does not wish to reuse the id, it MUST reject
   the creation with an "invalidProperties" SetError listing the id
   property; the client can then retry without an id to create the
   object with a new id.

   The server MUST reject a create with an id that is not the id of a
   destroyed object of the same type in the same account.  Apart from
   restoring a destroyed object in this way, the server MUST NOT reuse
   the id of a destroyed object for which it retains any history.

5.  Design Rationale

   This section is not normative.

   History is returned through the existing Foo/get method rather than a
   new method.  The entries in the list array are ordinary objects of
   the data type, so a client can feed them to the same parser it uses
   for current objects, and the existing properties argument works
   unchanged.  It also allows a client to fetch the final state of many
   destroyed objects in a single call, using a backreference to the
   destroyed list from Foo/changes, as shown in the examples above.

   The history metadata is embedded in each object as the objectHistory
   property, rather than wrapping each object in a separate container,
   for the same reason: the entries remain ordinary objects.  This
   follows the existing pattern of JMAP-specific properties such as id
   being added to objects whose format is defined elsewhere; the
   objectHistory name is reserved in the relevant property registries
   (see Section 6).

   Each entry is a full copy of the object rather than a patch against
   the previous version.  Patches would save space, but the client would
   need every intermediate version to reconstruct any one of them, which
   conflicts with the server's freedom to prune versions, and the final
   version of a destroyed object would need to be returned in full
   anyway.

   Every superseded version carries a replaced timestamp.  The server
   always knows, at least approximately, when it replaced a snapshot it
   was keeping, and the timestamp is what a client needs to present
   history to a user and to filter it with historyAfter.

6.  IANA Considerations

Gondwana                  Expires 19 April 2027                [Page 12]
Internet-Draft             JMAP Object History              October 2026

6.1.  JMAP Capability Registration for urn:ietf:params:jmap:object-
      history

   IANA is requested to register the "Object History" Capability as
   follows:

   Capability Name: urn:ietf:params:jmap:object-history

   Intended use: common

   Change Controller: IETF

   Specification document: this document

   Security and privacy considerations: this document, Security
   Considerations

6.2.  Update to the JMAP Data Types Registry

   This document adds a "Supports History" column to the "JMAP Data
   Types" registry [IANA-JMAP].  The value is "Yes" or "No", indicating
   whether the data type supports the history extensions defined in this
   document.

   The initial values for existing registrations are:

Gondwana                  Expires 19 April 2027                [Page 13]
Internet-Draft             JMAP Object History              October 2026

             +===========================+==================+
             | Type Name                 | Supports History |
             +===========================+==================+
             | Core                      | No               |
             +---------------------------+------------------+
             | PushSubscription          | No               |
             +---------------------------+------------------+
             | Mailbox                   | Yes              |
             +---------------------------+------------------+
             | Thread                    | No               |
             +---------------------------+------------------+
             | Email                     | Yes              |
             +---------------------------+------------------+
             | EmailDelivery             | No               |
             +---------------------------+------------------+
             | SearchSnippet             | No               |
             +---------------------------+------------------+
             | Identity                  | Yes              |
             +---------------------------+------------------+
             | EmailSubmission           | No               |
             +---------------------------+------------------+
             | VacationResponse          | Yes              |
             +---------------------------+------------------+
             | MDN                       | No               |
             +---------------------------+------------------+
             | Quota                     | No               |
             +---------------------------+------------------+
             | SieveScript               | Yes              |
             +---------------------------+------------------+
             | Principal                 | No               |
             +---------------------------+------------------+
             | ShareNotification         | No               |
             +---------------------------+------------------+
             | AddressBook               | Yes              |
             +---------------------------+------------------+
             | ContactCard               | Yes              |
             +---------------------------+------------------+
             | Calendar                  | Yes              |
             +---------------------------+------------------+
             | CalendarEvent             | Yes              |
             +---------------------------+------------------+
             | CalendarEventNotification | No               |
             +---------------------------+------------------+
             | ParticipantIdentity       | Yes              |
             +---------------------------+------------------+

                                 Table 1

Gondwana                  Expires 19 April 2027                [Page 14]
Internet-Draft             JMAP Object History              October 2026

   The default value for this column is "No".  New registrations that do
   not explicitly include a value are assumed not to support history.

   This column records whether history is meaningful for a data type.
   Whether a particular server supports history for a type is advertised
   in the supportedTypes capability property.

6.3.  JSContact Property Registration

   IANA is requested to register the following property in the
   "JSContact Properties" registry defined in [JSCONTACT], Section 3, so
   that the name cannot be used for a property of a JSContact Card.

   Property Name: objectHistory

   Property Type: not applicable

   Property Context: Card

   Intended Usage: reserved

   Since Version: 1.0

   Change Controller: IETF

   Reference: this document

6.4.  JSCalendar Property Registration

   IANA is requested to register the following property in the
   "JSCalendar Properties" registry defined in [JSCALENDAR],
   Section 8.2, so that the name cannot be used for a property of a
   JSCalendar object.

   Property Name: objectHistory

   Property Type: not applicable

   Property Context: Event, Task, Group

   Intended Usage: reserved

   Change Controller: IETF

   Reference: this document

Gondwana                  Expires 19 April 2027                [Page 15]
Internet-Draft             JMAP Object History              October 2026

   Specifications that define a JMAP data type whose object format is
   also governed by a separate property registry SHOULD reserve the
   objectHistory property name in that registry.

7.  Security Considerations

   All security considerations from [JMAP-CORE] apply to this document.

7.1.  Storage and Resource Consumption

   Retaining historical versions of objects can consume significant
   server storage.  Servers SHOULD impose reasonable limits on history
   retention, either by duration (advertised via maxHistoryDuration) or
   by total storage.  Servers MAY silently discard history entries when
   storage limits are reached.

7.2.  Access Control

   Historical versions of an object MUST respect the same access
   controls as the current version.  A user who does not have permission
   to read an object MUST NOT be able to read its history.

   Access rights can change over time, for example when an object is
   shared with a new user, or moved into a shared container.  The server
   MUST NOT return a historical version to a user who could not have
   read that version at the time it was current.  This requires the
   server to track changes to access rights.  A server that does not
   keep a full history of access rights can take a conservative
   approach: record the point at which the access rights affecting an
   object (or its containing object, such as a Mailbox or AddressBook)
   last changed, for example as a modseq, and not return any version
   that was replaced before that point.

   Similarly, for destroyed objects, the server MUST NOT reveal the
   existence or content of a destroyed object to a user who could not
   read it at the time it was destroyed.

7.3.  Information Disclosure

   Object history may reveal information that the user or an
   administrator intended to remove.  For example, a contact record that
   was updated to remove a phone number will still have that phone
   number visible in its history.  Servers SHOULD provide administrators
   with the ability to purge history for specific objects when required
   for privacy or compliance reasons.

Gondwana                  Expires 19 April 2027                [Page 16]
Internet-Draft             JMAP Object History              October 2026

7.4.  Privacy Considerations

   When a server supports this extension, updating or destroying an
   object does not immediately remove the previous content from the
   server; it remains retrievable by anyone with read access to the
   object until the server discards it under its retention policy.
   Users may not expect this.  This is not new in kind (servers commonly
   retain deleted data in backups, and many mail systems offer a
   "recently deleted" view), but this extension makes such data
   retrievable through the protocol.

   Servers SHOULD make their retention policy visible to users, for
   example through the maxHistoryDuration capability property and the
   server's user interface.  Clients that present a "destroy" operation
   to users SHOULD NOT describe it as irreversible or permanent when the
   server advertises this capability for that data type.  Whether, and
   how, a user can request that history be purged before the end of the
   retention period is a matter of server policy and is outside the
   scope of this document.

8.  Changes

   EDITOR: please remove this section before publication.

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

   *draft-ietf-jmap-object-history-02*

   *  Replaced the top-level hasMoreHistory response property with a
      per-entry objectHistory.hasOlder property, so the client can tell
      which ids have more history.

   *  Added historySkip, to skip the N most recent versions of each id;
      ids with N or fewer versions are returned in notFound.

   *draft-ietf-jmap-object-history-01*

   *  Removed "Updates: 8620"; the JMAP Data Types registry is not
      defined by RFC 8620.  The registry is now referenced by name
      rather than by its defining document.

   *  Corrected the registry table to match the current registry.

   *  historyLimit is now per id.

   *  Defined behaviour for ids: null with includeDestroyed, and
      clarified that "maxObjectsInGet" counts ids, not entries.

Gondwana                  Expires 19 April 2027                [Page 17]
Internet-Draft             JMAP Object History              October 2026

   *  Ids with no matching versions are returned in notFound.

   *  Which changes create a version, and the values of server-computed
      properties, are server dependent.

   *  Blobs referenced by returned versions must be downloadable.

   *  Expanded access control considerations for changing access rights.

   *  Added supportedTypes to the account capability; requesting history
      for an unsupported type is an error.

   *  Reserved the objectHistory property name in the JSContact and
      JSCalendar property registries.

   *  Added acknowledgements.

   *  Added a Design Rationale section (Foo/get rather than a new
      method, embedded metadata, full objects rather than patches,
      timestamps).

   *  Added a Restoring Objects section: a client may ask to recreate a
      destroyed object with its old id, and the server may accept or
      reject; ids of destroyed objects are otherwise not reused.

   *  maxHistoryDuration is now a best-effort statement of server
      policy.

   *  Clarified that "objectHistory" may appear in properties.

   *  Added Privacy Considerations.

   *  Added historyBefore for paging backwards through history; versions
      of the same object have distinct replaced values, with at most
      millisecond precision.

   *  Removed the audit use case from the introduction.

   *draft-ietf-jmap-object-history-00*

   *  Uploaded with working-group adopted name.

   *draft-gondwana-jmap-object-history-00*

   *  initial proposal

Gondwana                  Expires 19 April 2027                [Page 18]
Internet-Draft             JMAP Object History              October 2026

9.  Acknowledgements

   The author would like to thank Ben Bucksch, Mauro De Gennaro, Arnt
   Gulbrandsen, Neil Jenkins, and Robert Stepanek for their feedback on
   this document.

   {backmatter}

10.  References

10.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>.

10.2.  Informative References

   [IANA-JMAP]
              IANA, "JSON Meta Application Protocol (JMAP)", n.d.,
              <https://www.iana.org/assignments/jmap/>.

   [JMAP-CONTACTS]
              Jenkins, N., Ed., "JSON Meta Application Protocol (JMAP)
              for Contacts", RFC 9610, DOI 10.17487/RFC9610, December
              2024, <https://www.rfc-editor.org/rfc/rfc9610>.

   [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>.

   [JSCALENDAR]
              Jenkins, N. and R. Stepanek, "JSCalendar: A JSON
              Representation of Calendar Data", RFC 8984,
              DOI 10.17487/RFC8984, July 2021,
              <https://www.rfc-editor.org/rfc/rfc8984>.

Gondwana                  Expires 19 April 2027                [Page 19]
Internet-Draft             JMAP Object History              October 2026

   [JSCONTACT]
              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/rfc/rfc9553>.

Author's Address

   Bron Gondwana
   Fastmail
   Email: brong@fastmailteam.com

Gondwana                  Expires 19 April 2027                [Page 20]