Skip to content

Push

A client sends its local changes with POST /lve/v1/sync/push. The server judges every change on its own and answers with one result per change.

POST /lve/v1/sync/push HTTP/1.1
Authorization: Bearer 7|kM2pQ9xV3rT8wZ1yB6nH4jL0sD5fG7aC2eR9tY1u3f9a1c07
Content-Type: application/json
Accept-Language: en
{
"device_id": "3F2A9C1B-7E4D-4B2A-9C1E-5D8F0A2B4C6E",
"changes": [
{
"entity": "location",
"id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07",
"op": "upsert",
"hlc": "001791792000123-0000-3F2A9C1B",
"data": { "code": "N-A01", "name": "Shelf A01" }
},
{
"entity": "movement",
"id": "77AA0C41-2E9B-4F7D-8B1A-5D3C2E1F0A99",
"op": "upsert",
"hlc": "001791792000123-0002-3F2A9C1B",
"data": {
"type": "receipt",
"article_id": "8C1F3D2E-5B7A-4A61-9F0E-2B4C6D8E0A13",
"to_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07",
"quantity": 20,
"booked_at": "2026-10-12T08:00:00Z",
"booked_by": "Erika Mustermann"
}
}
]
}
Field Type Rules
device_id string Required, at most 64 characters. The device.id the client signed in with. The server records it with every applied change.
changes array Required (may be empty), at most 500 entries. Change envelopes in the order they were made.

A request with more than 500 changes is refused as a whole with 422 and too_many_changes; nothing is stored. A missing device_id or a changes value that is not an array gives 422 and validation_failed. A body that is not JSON gives 400 and malformed_request.

Clients send their outbox oldest first, in batches of at most 500, and upload the attachments a batch references before the batch (see Attachments).

{
"results": [
{ "id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "entity": "location", "status": "applied" },
{ "id": "77AA0C41-2E9B-4F7D-8B1A-5D3C2E1F0A99", "entity": "movement", "status": "ignored" }
]
}

results has exactly one entry per change, at the same index. Each entry:

Field Type Meaning
id string or null id of the change exactly as sent (not normalised); null when the change had no string id
entity string or null entity of the change as sent; null when the change had no string entity
status string applied, ignored or rejected
reason string Only for rejected, see below
message string Only for rejected: text for the user in the requested language
Status Meaning Client action
applied The change won and is now the current version. It is in the change log. Remove from the outbox
ignored The server already has this version, a newer one, or (for a movement) any version with this id. Nothing was stored. Remove from the outbox
rejected The server refuses the change. Nothing was stored. Keep, mark as rejected, show message

The server checks in this order and reports the first failure:

Reason Cause Example message (en)
invalid_change The change is not a JSON object The change must be a JSON object.
invalid_change entity missing, not a string, longer than 64 characters or not matching ^[a-z][a-z0-9_]*$ The entity may only contain lower-case letters, digits and underscores and must start with a letter.
invalid_change id missing or not a UUID The ID field must be a valid UUID.
invalid_change op not upsert or delete The selected operation is invalid.
invalid_change hlc missing or not matching the stamp pattern The version stamp must look like 001791792000123-0000-3F2A9C1B.
invalid_change upsert without an object in data An upsert needs data as JSON object.
invalid_change delete with data other than null A delete must not carry data.
immutable delete of a movement Movements cannot be deleted. Correct a wrong booking with a counter-booking.
invalid_payload data of a known entity type breaks the server's payload rules The SKU field is required.
locked Change to a document_template whose stored version has locked: true Your company sets this template on the server. It cannot be changed in the app.

message for invalid_change and invalid_payload names the first failing field. The payload rules of the reference server are listed per entity under "Server validation" (article and so on). Unknown entity types are never checked.

The set of reasons is closed in version 1. A client that receives an unknown reason treats it like invalid_payload.

  • Independent changes. A rejected change does not affect the others. The reference server stores each change in its own database transaction; when the server fails in the middle of a batch (5xx), the changes before the failure stay stored and the client resends the whole batch, getting ignored for those.
  • Order. Changes are processed in request order. Two changes for the same record in one batch are merged one after the other, so the later stamp wins as usual.
  • Idempotency. Pushing the same change again is answered ignored, because its stamp is not greater than the stored one. Clients resend a batch after a timeout without risk.
  • Concurrency. Two devices pushing the same record at the same time are serialised by the server; the merge decision and the write happen atomically. The reference server serialises all writes to its change log with one row lock (see Pull).
  • No semantic checks. The server does not check references (an article id that does not exist yet), stock levels or uniqueness of item numbers. Records arrive in any order and offline devices may legitimately create duplicates.
  • Payload storage. The server stores data as it was sent, apart from re-encoding the JSON (key order and number formats are kept, 1.0 stays 1.0). Unknown fields are kept.
  • Id and stamp normalisation. The reference server stores record ids and stamps in upper case. Pulled changes therefore show the upper-case form.

A malformed request as a whole is answered with an HTTP error and no results. A malformed change inside a well-formed request is answered with 200 and a rejected result. Clients therefore check the HTTP status first and the per-change results second.

Status Code Meaning
200 Results for every change
400 malformed_request Body is not JSON
401 unauthenticated Token missing or revoked
422 too_many_changes More than 500 changes
422 validation_failed device_id or changes missing or of the wrong type
5xx server_error Retry the batch later

The test vector test-vectors/push.json shows a batch with every outcome and the exact answer of the reference server.