Delta sinhronizacija
Večina partnerskih integracij vzdržuje lokalni odsev locco virov (zaposleni, vozila, stroškovna mesta, oddelki, potni nalogi, izplačila, webhook naročnine). Po začetnem polnjenju je vprašanje na vsakem endpointu enako: kako povprašati, kaj se je spremenilo od zadnje sinhronizacije, brez ponovnega branja vsake vrstice?
Odgovor je query parameter modifiedSince. Podprt je na vsakem paginiranem list endpointu in je primarni podprt način inkrementalne sinhronizacije s partnerskim API-jem v v1.
Na kratko
- Vsak paginiran list endpoint sprejme
?modifiedSince=<ISO 8601 UTC časovna oznaka>. - Strani se vrnejo razvrščene
(updatedAt, id)ASC, ko jemodifiedSincenastavljen brez izrecnegasortparametra. To je vrstni red, ki ga partnerji potrebujejo za prehod skozi časovno premico sprememb brez ponovnega branja že obdelanih vrstic. - Velikost strani je omejena na 100 na strani strežnika. Večje vrednosti se tiho omejijo,
pageSizev odgovoru pa odraža omejeno vrednost (ne surovi zahtevek), tako daMath.ceil(total / pageSize)aritmetika ostane pravilna. - Kazalec je
updatedAtnajnovejše vrstice, vrnjene s prejšnjim uspešnim povpraševanjem. Shranite ga. Posredujte ga ob naslednjem povpraševanju. Ponovite.
Endpointi
Ista pogodba modifiedSince je podprta na:
| Vir | Endpoint |
|---|---|
| Zaposleni | GET /api/v1/employees |
| Vozila | GET /api/v1/vehicles |
| Stroškovna mesta | GET /api/v1/cost-centers |
| Oddelki | GET /api/v1/departments |
| Potni nalogi | GET /api/v1/travel-entries |
| Izplačila | GET /api/v1/payouts |
| Webhook naročnine | GET /api/v1/webhooks |
Webhook dostave (GET /api/v1/webhooks/{id}/deliveries) namerno NE podpirajo modifiedSince. Dostave so samo dodajane in efektivno nespremenljive po shranjevanju, zato partnerji, ki želijo slediti stanju dostav, povprašujejo po id-u ali po filtru statusa.
Kaj modifiedSince vrača
Za povpraševanje, kot je:
GET /api/v1/employees?modifiedSince=2026-04-30T12:00:00ZAuthorization: Bearer locco_live_...X-Company-Id: 9c4e0d70-9d59-4a4a-a7d9-1e5ff6f1e3ecStrežnik vrne vsako vrstico, katere updatedAt je v trenutku ali po posredovani časovni oznaki. updatedAt se na strani strežnika računa kot vrednost editedAt vrstice, s povratkom na createdAt za vrstice, ki niso bile nikoli urejene, tako da je časovna oznaka vedno izpolnjena.
Vrstica, ki je bila ustvarjena ob 2026-04-30T11:55:00Z in nikoli urejena, je izključena (njen updatedAt ob 11:55:00Z je pred kazalcem 12:00:00Z). Vrstica, ki je bila ustvarjena lani, vendar urejena danes, je vključena.
Jamstvo razvrščanja
Ko je modifiedSince nastavljen brez izrecnega sort parametra, strežnik razvrsti stran po (updatedAt ASC, id ASC). To je ključno za pravilnost delta sinhronizacije:
- Brez
idrazrešilca enakih vrednosti bi se dve vrstici z enakimupdatedAtskozi zahtevke pojavili nedeterministično, partnerji, ki gredo skozi paginacijo, pa bi tiho preskakovali ali podvajali vrstice. - ASC (najstarejša sprememba prva) pomeni, da je partnerjev kazalec na koncu strani največji
updatedAt, ki ga je videl, kar je naravna izhodiščna točka za naslednje povpraševanje.
Če posredujete izrecen sort=...&sortDir=desc, strežnik še vedno doda id razrešilec, da paginacija ostane stabilna, vendar vrstni red strani ne bo tisti, ki ga želite za delta sinhronizacijo. Za sinhronizacijske primere uporabe pustite sort nenastavljen.
Omejitev paginacije
Vsak list endpoint omeji pageSize na 100 na strani strežnika. Polje pageSize v odgovoru odraža EFEKTIVNO omejeno vrednost, ne tega, kar ste zahtevali, zato:
GET /api/v1/employees?pageSize=10000vrne do 100 elementov IN telo odgovora, ki pravi "pageSize": 100. Odgovor nosi tudi polje totalPages, izračunano pri EFEKTIVNI omejeni velikosti strani, zato je najpreprostejši zaključilec zanke page >= totalPages. Izogibajte se ponovnemu računanju števila strani iz total / pageSize glede na vašo surovo vrednost zahtevka: izračunali boste premalo strani in tiho izgubili podatke.
page je podobno omejen na minimum 1; nepozitivne vrednosti strani se normalizirajo. Polje page v odgovoru odraža dejansko vrnjeno stran.
Zanka povpraševanja
Pogodba je preprosta: preberite shranjeni kazalec, povprašujte z njim, prehodite vsako stran, shranite največji updatedAt iz prehoda kot naslednji kazalec.
import timeimport requests
API = "https://api.locco.hr"HEADERS = { "Authorization": "Bearer locco_live_<your-key>", "X-Company-Id": "<your-company-id>",}
def poll_employees(cursor: str | None) -> tuple[list[dict], str]: """ Prehodi vsako stran zaposlenih, spremenjenih od `cursor`. Vrne vrstice plus nov kazalec (največji updatedAt med prehodom). """ rows = [] page = 1 new_cursor = cursor while True: params = {"page": page, "pageSize": 100} if cursor is not None: params["modifiedSince"] = cursor r = requests.get(f"{API}/api/v1/employees", headers=HEADERS, params=params) r.raise_for_status() body = r.json() rows.extend(body["items"])
# Premakni kazalec na največji updatedAt na tej strani. for row in body["items"]: updated_at = row.get("updatedAt") or row.get("createdAt") if updated_at and (new_cursor is None or updated_at > new_cursor): new_cursor = updated_at
# Uporabi totalPages iz odgovora, NE vrednosti, izračunane iz zahtevka, # tako da se zanka pravilno konča, tudi če je strežnik omejil pageSize. if page >= body["totalPages"]: break page += 1
return rows, new_cursor
# Prvo izvajanje: cursor=None potegne vse (popolno polnjenje).rows, cursor = poll_employees(cursor=None)save_to_local_db(rows)save_cursor(cursor)
# Stalno stanje: povprašuj vsakih N minut s shranjenim kazalcem.while True: time.sleep(300) # 5 minut cursor = load_cursor() rows, new_cursor = poll_employees(cursor=cursor) save_to_local_db(rows) save_cursor(new_cursor)Nekaj subtilnosti, ki jih je vredno omeniti:
- Kazalec shranite šele po uspešnem celotnem povpraševanju, vključno s pisanjem vrstic v vašo lokalno shrambo. Sicer zrušitev sredi izvajanja izgubi vrstice, ki ste jih pridobili, a niste shranili, naslednje povpraševanje pa jih ne bo ponovno pridobilo.
- Kazalec je ekskluziven na strani strežnika, inkluziven na vaši meji. Vrstica z
updatedAt == cursorse bo pojavila v naslednjem povpraševanju. Idempotentni upserti na vaši strani absorbirajo ponovno branje. Če želite strogo ekskluzivnost, premaknite svoj shranjeni kazalec nacursor + 1mspo uspešnem prehodu. - Sočasne spremembe so eventualno konsistentne. Vrstica, urejena sredi povpraševanja, se lahko pojavi na poznejši strani (ker je njen
updatedAtnapredoval čez mejo strani). Vaša upsert logika bo prejšnji prikaz prepisala z novejšim. Ročno reševanje konfliktov ni potrebno.
Začetno polnjenje
Pri prvem izvajanju posredujte modifiedSince=null (ali izpustite). Strežnik vrne vsako vrstico v podjetju. Pri velikosti strani 100 vrstic ima podjetje s 5000 zaposlenih 50 strani. Prilagodite ritem povpraševanja glede na rezervo omejitve zahtevkov, ki jo imate (glej Omejitve zahtevkov), in shranite kazalec na koncu.
Za zelo velika začetna polnjenja partnerji včasih raje uporabijo začetno vrednost modifiedSince=2000-01-01T00:00:00Z, da uporabljajo isto pot kode kot za povpraševanje stalnega stanja. Funkcionalno enako kot izpustitev parametra.
Kombiniranje z webhooki
Webhooks (glej Webhooks) potiska obvestila o dogodkih, ko se sproži domenski dogodek. So priporočen način odzivanja na spremembe v realnem času. Vendar so “best-effort”: dostava lahko propade (naročnik nedostopen, omrežna težava) in po izčrpanju politike ponovnih poskusov je dogodek izgubljen.
Delta sinhronizacija je trajnostni partner. Običajen vzorec:
- Webhooks za zakasnitev. Reagirajte na dogodke v sekundah po sproženju.
- Urna delta sinhronizacija kot varnostna mreža. Ujemite, kar koli so webhooki spustili (neuspele dostave, naročnina kratko onemogočena, prekinitev na strani partnerja).
Obe površini delita isti podatkovni model. Webhook telesa nosijo id-je virov, partnerji pa potegnejo celoten vir prek GET /api/v1/{resource}/{id} za trenutno stanje. Pull endpoint je edini vir resnice v obeh tokovih.