SharePointVault ~/gotchas/lists ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdM↓ list-items-keep-authors-dates-and-version-history.md
sharepoint-vault›gotchas›lists›list-items-keep-authors-dates-and-version-history.mdhistoryedit on GitHub
● GOTCHAlists· 6 min read

Migrating list items with original authors, dates — and their version history

applies to: SharePoint Onlinereviewed: 2026-11-12#lists#migration#rest-api#metadata#authorship#versioning#content-approval

The Copy and Move API (CreateCopyJobs) handles files and folders only. For list items, create each one with AddValidateUpdateItemUsingPath and pass Author, Editor, Created and Modified in the same call — all four stick and the item has a single version. Older versions can be replayed: one ValidateUpdateListItem call per version with bNewDocumentUpdate: false, carrying that version's values plus its Editor and Modified. Times are kept to the minute, not the second. Always read the item back: two value formats are discarded with HTTP 200 and no error. This needs high rights: for a user who may only add items, SharePoint silently replaces the four values with the caller and the current time.

Copy and Move API (CreateCopyJobs) umí jen soubory a složky. Položky seznamu se přenášejí po jedné: AddValidateUpdateItemUsingPath s Author, Editor, Created a Modified v témže volání – všechny čtyři drží a položka má jedinou verzi. Starší verze jde přehrát: za každou verzi jedno ValidateUpdateListItem s bNewDocumentUpdate: false, hodnotami té verze a jejím Editor a Modified. Časy drží na minutu, ne na vteřinu. Položku vždy přečíst zpátky: dva tvary hodnot se zahodí s HTTP 200 a bez chyby. Chce to vysoká práva: uživateli, který smí jen přidávat, SharePoint všechny čtyři hodnoty tiše nahradí volajícím a aktuálním časem.

All results below were measured on 2026-10-27 on a custom list with versioning on (Czech locale, web time zone Pacific). The author and editor were deliberately a different account than the caller, so a match proves something. Every verdict comes from reading the item back, not from the response.

Create with all four fields — one call, one version

await fetch(`${web}/_api/web/GetList(@a)/AddValidateUpdateItemUsingPath?@a='${encodeURIComponent(listRel)}'`, {
  method: 'POST',
  headers: { Accept: 'application/json;odata=nometadata',
             'Content-Type': 'application/json;odata=nometadata', 'X-RequestDigest': digest },
  body: JSON.stringify({
    listItemCreateInfo: { FolderPath: { DecodedUrl: listRel }, UnderlyingObjectType: 0 },
    formValues: [
      { FieldName: 'Title',    FieldValue: 'Request 42' },
      { FieldName: 'Author',   FieldValue: JSON.stringify([{ Key: 'i:0#.f|membership|alice@contoso.com' }]) },
      { FieldName: 'Editor',   FieldValue: JSON.stringify([{ Key: 'i:0#.f|membership|alice@contoso.com' }]) },
      { FieldName: 'Created',  FieldValue: '4.3.2019 1:15' },   // web locale AND web time zone
      { FieldName: 'Modified', FieldValue: '19.7.2021 7:42' }
    ],
    bNewDocumentUpdate: false
  })
});

Read back: AuthorId, EditorId, Created, Modified all as sent, OData__UIVersionString 1.0, and the single version carries the foreign editor. The date must be in the web's locale and time zone — see Created/Modified on a document library for how to build it with utctolocaltime.

Who can set them: not a user who may only add items

The measurements above ran as a site owner. On 2026-11-12 the same call was sent by a user whose only right on the list was a custom add-only level (Add Items + View Items, item-level read and edit limited to their own items), with a foreign Author and Editor and an old Created. The response was HTTP 200 with HasException: false on every field — and the item read back with the caller as Author and Editor, Created = the time of the call, version 1.0. SharePoint silently replaced the values instead of rejecting them.

So in an add-only list the server's Author and Created do show who wrote the row and when, even if the client tries to send something else. Two limits: Contribute and Edit levels were not measured here, and anyone with Full Control can backdate a row exactly as shown above.

Creating a bare item first and then sending the four fields with ValidateUpdateListItem also works with bNewDocumentUpdate: true. With false you get a version 2.0, and version 1.0 keeps the caller and today's time in the history. Creating with all four at once avoids both.

Version history can be replayed

  1. Create the item with version 1's values and all four fields (Modified = Created).
  2. For every later version, call items(id)/ValidateUpdateListItem with bNewDocumentUpdate: false, that version's field values, its Editor and its Modified.

Measured with three versions: the target shows 3.0, 2.0 and 1.0 with the right titles, the right editor on each (a different person on v2) and the right timestamps (to the minute). Created By and Created stay those of version 1. Send the timestamps in increasing order.

Attachments add a version

items(id)/AttachmentFiles/add(FileName='…') creates a new version and stamps Editor and Modified with the caller and now. Sending all four fields again with bNewDocumentUpdate: true restores the values, but the extra version stays in the history (with the restored metadata). Upload attachments before the last replayed version, or accept one extra version.

Content approval: approve in the same call

On a list with content approval, a new item is _ModerationStatus 2 (pending). Add { FieldName: '_ModerationStatus', FieldValue: '0' } to the call that carries the four fields (bNewDocumentUpdate: true). The result is approved, the four fields hold, and there is still one version.

Do not approve with a MERGE on OData__ModerationStatus. It approves, but stamps Editor/Modified with the caller. When the four fields are sent afterwards to fix that, the item goes back to pending and gains a version.

Value formats — and two that fail without a word

Each format was sent alone, because one rejected field cancels the whole update.

field type accepted rejected
Number 1,5 (the web's decimal separator) 2.5 → HasException "Only numbers can go here."
DateTime 6.5.2020 0:08 (web locale and zone) ISO → "You must specify a valid date…"
MultiChoice A;#B, ;#B;#C;# —
UserMulti [{"Key":"…"},{"Key":"…"}] —
Lookup 1, 2;#Title —
LookupMulti 1;#;#2;#, 2;#L2;#1;#L1 ⚠ 1;#2 → stores only the first value, no error
URL https://…, description —
Boolean 1, No ⚠ Yes → stores No, no error

The two ⚠ rows look like success: HTTP 200, no HasException, a different value. Send Boolean as 1/0 and LookupMulti as id;#;#id;#. Compare the column values you read back with what you sent, not only the author and dates.

Over the 5,000 list view threshold: writing is the same, reading is not

5,100 items created with all four fields from one browser at concurrency 4 took 550 s, about 560 items per minute. There were seven 429 responses: after roughly 2,800 items SharePoint throttled for about two minutes (Retry-After), then continued at the same pace. No item failed. Creating items and replaying history past item 5,000 behaved exactly as on a small list.

Reading does change:

query on 5,100 items result
$filter on a non-indexed column (Modified, Title) 500 SPQueryThrottledException
$filter=Id gt 4900 and Modified ge … 500 – an indexed first clause does not save it
$orderby=Id asc&$top=5000 + nextLink 200, pages fine
no $filter, $top=5000 200 with nextLink

Page by Id and filter in code. An incremental "since last run" mark works as Id gt N, but not as a Modified filter.

Count what you wrote from your own run log, or with $orderby=Id desc&$top=1. If you read ItemCount during the run, fetch it with cache: 'no-store': repeating the same GET URL in the browser kept returning an early value (87 when more than 600 items existed). That was most likely the browser cache, not SharePoint.

Rule

One item = one AddValidateUpdateItemUsingPath with the four fields (plus _ModerationStatus if needed). Replay older versions with bNewDocumentUpdate: false. Then read everything back. Expect minute precision and an extra version for each attachment step.

Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last reviewed: 2026-11-12last updated: 2026-11-12historyedit on GitHubview source