POST/documents/{documentId}/sync
Sync encrypted Loro updates
Operation documents.sync
Description
Uploads encrypted Loro updates with their signed write headers and returns the updates the client is missing from its version vector, one bounded page at a time with a continuation cursor. Writing requires write access through an authorizing container path; reading requires read access. A retried update id is accepted only when its ciphertext and header are identical; 409 responses carry a code that tells the client whether to replan, regenerate, recover, or stop, and 413 means the request is too large.
Authentication
Requires a signed-in session: the client sends its session token as a bearer token in the Authorization header.
Parameters
| Name | In | Type | Details |
|---|---|---|---|
documentId | path | string | required |
Request body
application/jsonDocumentSyncRequest
A document sync: uploads encrypted updates and asks for the ones the client is missing, one page at a time.
13 fields
Container paths that authorize a write; reads may omit them.
containerRekeyslist of objectoptional- at most 16 items
Container rekeys to commit first in the same transaction.
16 fields
bodyany JSON valuerequired- at most 4,096 items
- at most 100 items
eventobject with any fieldsrequiredexpectedManifestHashstringrequired- not empty
keyEpochobject with any fieldsrequiredkeyringobject with any fieldsrequirednullablemanifestobject with any fieldsrequired- at most 100 items
parentKekRefobjectoptionalnullable3 fields
containerIdstringrequired- not empty
containerKeyEpochIdstringrequired- not empty
keyEpochHashstringrequired- exactly 64 characters
- lowercase hexadecimal
Other fields are allowed.
predecessorBridgeobject with any fieldsrequirednullable- at most 100 items
userRecipientKeyslist of object with any fieldsoptionalwrapslist of object with any fieldsrequired
Other fields are allowed.
A new content-key bundle, when the write advances the content-key epoch.
contentKeyEpochintegerrequired- greater than 0
Content-key epoch the client is writing and reading at.
expectedLinkSetManifestHashstringrequired- not empty
The document link-set manifest head the client planned against; a different current head is refused as stale.
expectedTargetHashstringrequired- not empty
The key target hash the client planned against.
historyModestringoptional- always
raw
rawasks for every retained update missing from the frontier, bypassing rotation baselines; read-only.- always
inlineRekeyCommitIdstringoptional- exactly 64 characters
- lowercase hexadecimal
Marker that makes carried rekeys and the write replay-safe as one transaction; present exactly when
containerRekeysis non-empty.localVersionVectorstringrequirednullable- at most 16,777,216 characters
The client's current version vector; the response returns updates it does not cover.
minLsnstringoptional- pattern
^[0-9A-Fa-f]{1,8}\/[0-9A-Fa-f]{1,8}$
Commit LSN the server must have reached before answering, for read-after-write consistency.
- pattern
outgoingUpdateslist of objectrequired- at most 64 items
Encrypted updates to upload.
9 fields
checkpointKindstringoptional- always
rotate_baseline
- always
checkpointPayloadKindstringoptional- always
full_history_snapshot
- always
encryptedDatastringrequired- 1 to 16,777,216 characters
idstringrequired- lowercase UUID v4
partialEndVersionVectorstringrequired- 1 to 16,777,216 characters
partialStartVersionVectorstringrequired- 1 to 16,777,216 characters
plaintextHashstringrequired- not empty
sourceVersionVectorstringoptional- 1 to 16,777,216 characters
writeHeaderobject with any fieldsrequired
Other fields are allowed.
pullCursorstringoptional- 1 to 512 characters
Continuation cursor from the previous page; a continuation is read-only.
supportsPullPaginationbooleanrequired- always
true
Always true; every response is paged.
- always
Other fields are allowed.
Responses
200 OK
application/jsonDocumentSyncResponse
The result of a sync: which uploads were accepted, one page of missing updates, and the key material to decrypt them.
9 fields
acceptedOutgoingUpdateIdslist of stringrequired- at most 64 items
Ids of uploaded updates that are now committed.
commitLsnstringrequirednullableCommit LSN after the request; pass it as
minLsnto read your own writes from a replica.commitLsnModestringrequired- one of
tracked,untracked
trackedfor a durable backend position;untrackedwith the0/0sentinel when the backend cannot report one.- one of
Content-key bundle of the current epoch.
Bundles of older epochs that this page's updates were written at; never the current epoch.
documentIdstringrequiredThe document's current key targets.
- at most 64 items
Encrypted updates the client's version vector does not cover.
Other fields are allowed.
Other responses
| Status | Meaning | Body |
|---|---|---|
| 400 | Bad Request |
|
| 401 | Unauthorized |
|
| 402 | Payment Required |
|
| 403 | Forbidden |
|
| 404 | Not Found |
|
| 409 | Conflict |
|
| 413 | Serialized document sync requests are limited to 16777216 bytes |
|
| 500 | Internal Server Error |
|
| 503 | Service Unavailable |
|
Additional rules
The server also enforces these rules, which the schema above cannot express.
inlineRekeyCommitIdmust be present exactly whencontainerRekeysis non-empty.authorizingContainerPathRefsis required whenoutgoingUpdatesis non-empty.containerRekeysmay be non-empty only whenoutgoingUpdatesis non-empty.Outgoing update ids must be unique within the request.
A request carrying
pullCursormust not contain outgoing updates, container rekeys, a content-key bundle, or authorizing paths.historyMode"raw" must not be combined with outgoing updates, container rekeys, a content-key bundle, or authorizing paths.historyMode"raw" requireslocalVersionVectorto be null so the complete retained history is requested.checkpointKind,checkpointPayloadKind, andsourceVersionVectormust be absent together or form a rotation baseline.checkpointKind,checkpointPayloadKind, andsourceVersionVectormust be absent together or form a rotation baseline.commitLsnMode"untracked" requires thecommitLsn"0/0" sentinel.commitLsn"0/0" requirescommitLsnMode"untracked" in the same response.pullPage.hasMoreis true exactly whenpullPage.nextCursoris non-null.contentKeyBundlescarries each content-key epoch at most once, and only epochs older thancontentKeyBundle.contentKeyEpoch.