Anleitungen
Anleitung: Abgleich mit anderer Software
Ein verlässlicher Abgleich hat drei Teile: eine vollständige Erstbefüllung, regelmäßige Deltas und Webhooks, die das Delta früher auslösen. Schreibzugriffe zurück nach STRYK nutzen ETags, damit du keine Änderung der Rezeption überschreibst.
1. Erstbefüllung
Lies alle Seiten jeder Liste mit dem maximalen limit von 200. Listen sind nach updatedAt, dann id sortiert; der Cursor bleibt stabil, während sich Daten ändern. Merke dir je Ressource das neueste gesehene updatedAt, deine Hochwassermarke.
import { stryk } from './stryk.js';
// Initial load: walk every page, then remember the newest updatedAt you saw.
export async function fullLoad(resource, upsert) {
let cursor = null;
let highWaterMark = null;
do {
const params = new URLSearchParams({ limit: '200', ...(cursor ? { cursor } : {}) });
const { data: page } = await stryk(`/${resource}?${params}`);
for (const item of page.data) {
await upsert(item);
if (!highWaterMark || item.updatedAt > highWaterMark) highWaterMark = item.updatedAt;
}
cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);
return highWaterMark; // store it per resource, e.g. in your database
}from stryk import stryk
def full_load(resource, upsert):
cursor, high_water_mark = None, None
while True:
params = {"limit": 200, **({"cursor": cursor} if cursor else {})}
page, _ = stryk(f"/{resource}", params=params)
for item in page["data"]:
upsert(item)
if high_water_mark is None or item["updatedAt"] > high_water_mark:
high_water_mark = item["updatedAt"]
if not page["hasMore"]:
return high_water_mark
cursor = page["nextCursor"]2. Deltas mit updated_since
updated_since liefert alles mit updatedAt größer oder gleich dem angegebenen Zeitpunkt. Durch den inklusiven Vergleich geht nichts verloren, was in derselben Millisekunde geändert wurde; dafür kommen Einträge an der Marke erneut. Dedupliziere per id und updatedAt.
// Delta run: updated_since is inclusive (>=). Items at the mark arrive again, that is intended.
export async function syncChanges(resource, store) {
const since = await store.highWaterMark(resource);
let cursor = null;
let newest = since;
do {
const params = new URLSearchParams({ limit: '200', updated_since: since, ...(cursor ? { cursor } : {}) });
const { data: page } = await stryk(`/${resource}?${params}`);
for (const item of page.data) {
const known = await store.get(resource, item.id);
// Deduplicate by id + updatedAt; never overwrite newer local data with an older copy.
if (!known || item.updatedAt > known.updatedAt) await store.put(resource, item);
if (item.updatedAt > newest) newest = item.updatedAt;
}
cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);
await store.setHighWaterMark(resource, newest);
}def sync_changes(resource, store):
since = store.high_water_mark(resource)
cursor, newest = None, since
while True:
params = {"limit": 200, "updated_since": since, **({"cursor": cursor} if cursor else {})}
page, _ = stryk(f"/{resource}", params=params)
for item in page["data"]:
known = store.get(resource, item["id"])
if known is None or item["updatedAt"] > known["updatedAt"]:
store.put(resource, item)
newest = max(newest, item["updatedAt"])
if not page["hasMore"]:
break
cursor = page["nextCursor"]
store.set_high_water_mark(resource, newest)3. Webhooks als Auslöser
Webhooks verkürzen die Verzögerung: Bei booking.updated liest du diese Buchung oder startest das Delta für Buchungen. Baue keinen Zustand allein aus Ereignissen auf, Zustellungen können verspätet, doppelt oder nach Ende der Wiederholungen ganz ausbleiben.
| Ereignis | Neu lesen |
|---|---|
booking.created, booking.updated, booking.cancelled | GET /bookings/{id} |
guest.updated | GET /guests/{id} |
availability.changed | GET /availability für roomTypeIds, from, to |
rate.changed | GET /rates für denselben Zeitraum |
payment.updated | GET /bookings/{bookingId}/payments mit data.bookingId |
4. Regelmäßiger Abgleich
Lass das Delta zeitgesteuert laufen, auch wenn Webhooks funktionieren, für Buchungen typischerweise alle 5 bis 15 Minuten, für Kategorien und Raten stündlich. Eine nächtliche Erstbefüllung der gespiegelten Ressourcen fängt den Rest ab, etwa Löschungen in deinem eigenen System.
Bleibe unter dem Limit deines Schlüssels (standardmäßig 120 Anfragen je Minute, GET /me → rateLimit). Mit 200 Einträgen je Seite braucht ein Delta über Tausende Änderungen nur wenige Anfragen; eine Erstbefüllung verteilst du über einige Minuten. Siehe Rate-Limits.
5. Schreiben mit ETag und If-Match
Einzelressourcen liefern einen ETag-Header und denselben Wert als version. PATCH verlangt If-Match mit dem gelesenen ETag. Hat sich die Ressource inzwischen in STRYK geändert, wird der Schreibzugriff mit 412 precondition_failed abgelehnt; ohne If-Match erhältst du 428 precondition_required. If-None-Match bei einem GET liefert 304, wenn sich nichts geändert hat.
GET /api/partner/v1/guests/5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c
→ 200 OK
ETag: "7e5d3c1b"
{ "id": "5e4d3c2b-…", "phone": "+43 660 1234567", "version": "7e5d3c1b", … }
PATCH /api/partner/v1/guests/5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c
If-Match: "7e5d3c1b"
{ "phone": "+43 660 7654321" }
→ 200 OK ETag: "9f8e7d6c"
→ 412 precondition_failed (changed in STRYK meanwhile: read again)
→ 428 precondition_required (If-Match missing)// Optimistic concurrency: read, change, write with If-Match.
export async function updateGuestPhone(guestId, phone) {
for (let attempt = 1; attempt <= 3; attempt += 1) {
const { data: guest, etag } = await stryk(`/guests/${guestId}`);
try {
const { data: updated } = await stryk(`/guests/${guestId}`, {
method: 'PATCH',
headers: { 'If-Match': etag },
body: { phone },
});
return updated;
} catch (error) {
// 412: someone changed the guest in STRYK since your read. Read again and re-apply.
if (error.code !== 'precondition_failed') throw error;
}
}
throw new Error('Guest keeps changing; resolve manually.');
}def update_guest_phone(guest_id, phone):
for _ in range(3):
guest, headers = stryk(f"/guests/{guest_id}")
try:
updated, _ = stryk(f"/guests/{guest_id}", method="PATCH",
headers={"If-Match": headers["ETag"]}, json={"phone": phone})
return updated
except StrykError as error:
if error.code != "precondition_failed":
raise # 412 means: read again and re-apply
raise RuntimeError("Guest keeps changing; resolve manually.")6. Konflikte lösen
- STRYK ist führend für Buchungen, Preise, Verfügbarkeit und Zahlungen. Spiegle diese Daten; korrigiere sie nicht von außen.
- Für Gastkontaktdaten legst du je Feld fest, welches System gewinnt. Bei
412liest du neu, übernimmst nur deine Felder und schreibst mit dem neuen ETag. - Brich nach wenigen Versuchen ab und markiere den Datensatz für eine Person, statt endlos zu wiederholen.
- Wiederhole
409 booking_not_modifiablenie: Die Buchung ist eingecheckt, ausgecheckt oder storniert.