A sync session
This page walks through one complete sync round between a client and a server. Each step links to the page that specifies it.
Connecting for the first time
Section titled “Connecting for the first time”sequenceDiagram
autonumber
participant C as Client
participant S as Server
C->>S: GET /lve/v1/info
S-->>C: protocol, versions, auth, features, license
Note over C: check protocol = lve-sync and 1 in versions,<br/>verify the licence (if any)
alt username and password
C->>S: POST /lve/v1/auth/token {username, password, device}
else pairing code from a QR code
C->>S: POST /lve/v1/auth/pair {code, device}
end
S-->>C: token, user, inventory
Note over C: store the token securely,<br/>forget any old cursor,<br/>queue every local record for push
C->>S: first sync round (below)
- Discovery: the client reads
GET /lve/v1/infowithout a token and refuses the server unlessprotocolislve-syncandversionscontains1. It checks the organisation licence if the server sends one. - Authentication: the client signs in with a username and password or with a pairing code and receives a bearer token bound to its device id.
- The client drops any pull cursor it had (it belongs to another server or to an earlier connection) and puts every record it holds into its outbox, so that data created before the connection reaches the server. Records the server already has with the same or a newer version are answered with
ignoredand cost nothing else.
One sync round
Section titled “One sync round”sequenceDiagram
autonumber
participant C as Client
participant S as Server
C->>S: GET /lve/v1/info
S-->>C: license (may have changed)
loop until the outbox is empty
loop every attachment the next 500 outbox entries reference
C->>S: HEAD /lve/v1/attachments/{sha256}
alt 404 Not Found
C->>S: PUT /lve/v1/attachments/{sha256} (bytes)
S-->>C: 201 Created
end
end
C->>S: POST /lve/v1/sync/push {device_id, changes[<=500]}
S-->>C: results[] (applied, ignored, rejected)
Note over C: drop applied and ignored entries,<br/>mark rejected entries
end
loop while has_more
C->>S: GET /lve/v1/sync/pull?cursor=...&limit=500
S-->>C: changes[], cursor, has_more
Note over C: merge the page, then store the cursor
end
loop every referenced attachment missing locally
C->>S: GET /lve/v1/attachments/{sha256}
S-->>C: bytes (verify the SHA-256)
end
opt server numbering, fewer than 5 numbers left
C->>S: POST /lve/v1/number-ranges/delivery_note/reserve {count: 25}
S-->>C: numbers[]
end
- Licence refresh. The client reads
GET /lve/v1/infoagain on every round, because the organisation licence can be renewed or withdrawn at any time. If the licence no longer covers server sync, the round stops here and local changes wait. See Organisation licence. - Push. The client takes up to 500 entries from its outbox, oldest first. It uploads every attachment those changes reference that the server does not have yet, then pushes the changes. It removes entries answered
appliedorignoredand marks entries answeredrejected. It repeats until the outbox holds only rejected entries. See Push and Attachments. - Pull. Starting from its stored cursor (none on the first round), the client pulls pages of changes and merges each page with the merge rules. It stores the returned cursor after every page and pulls again while
has_moreis true. See Pull. - Attachment download. The client downloads every attachment that a record it holds references and that it does not have yet, and keeps only bytes whose SHA-256 matches.
- Number reservation. If delivery notes are numbered by the server and the client holds fewer than 5 reserved numbers, it reserves 25 more. See Number ranges.
Push comes before pull so that the client's own changes are in the server's log before it reads the log; the pull then also returns them, which is harmless.
When the reference app syncs
Section titled “When the reference app syncs”The reference app runs one round at a time; a round requested while another runs waits for it.
| Trigger | Delay |
|---|---|
| App start, and connecting to a server | immediately |
| A local change (outbox grew) | 1.5 seconds after the last change, so several edits cause one round |
| Timer while the app runs | every 60 seconds |
| The network becomes available | immediately |
A server should expect this traffic pattern: a short info, push and pull sequence per device about once a minute, more while users work.
How a round ends
Section titled “How a round ends”| Outcome | Client state |
|---|---|
| All steps succeeded | up to date, with the time of the round |
| A transport error (no network, timeout, DNS, TLS) | offline; the next trigger retries |
401 on any authenticated request |
signed out: the user has to sign in again; local data and the outbox stay |
| The licence or in-app plan does not cover server sync | paused: nothing is sent or received until the plan is back |
| Any other error | failed, with the server's error.message; the next trigger retries |
Nothing in a failed round is lost: unsent changes stay in the outbox, and the cursor only moves after a page was merged.