Flavoso APIGrundlagenBlättern und Filtern

Blättern und Filtern

Listen kommen seitenweise mit Cursor. Mit Filtern, updated_since und acknowledged holst du zuverlässig nur das Neue.

Listen

Jede Liste hat dieselbe Form: object: "list", die Einträge in data, dazu has_more und next_cursor. limit wählt 1 bis 100 Einträge je Seite, die Vorgabe ist 25. Bei GET /events heißt der Zeiger after und die Vorgabe ist 50.

Gibt es mehr, steht has_more: true da. Dann schickst du next_cursor als cursor zurück, bis has_more falsch ist. Der Cursor ist undurchsichtig: zurückgeben, nicht selbst bauen, nicht speichern.

Der Cursor trägt die Filter nicht mit. Schick dieselben Filter auf jeder Seite mit, sonst blätterst du in einer anderen Liste.
# First page: the filters go along on every page
curl "https://api.flavoso.com/v1/orders?created_since=2026-10-03T22:00:00Z&created_until=2026-10-04T22:00:00Z&limit=100" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# has_more is true: pass next_cursor back, with the SAME filters
curl "https://api.flavoso.com/v1/orders?created_since=2026-10-03T22:00:00Z&created_until=2026-10-04T22:00:00Z&limit=100&cursor=NEXT_CURSOR" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Filter

Mehrere Werte eines Filters trennst du mit Kommas: status=pending,confirmed.

ListeFilterReihenfolge
GET /ordersstatus, type, paid, acknowledged, updated_since, created_since, created_until, external_idZuletzt geändert zuerst; mit updated_since aufsteigend nach updated_at.
GET /reservationsstatus, updated_since, starts_from, starts_untilZuletzt geändert zuerst; mit starts_from oder starts_until aufsteigend nach starts_at.
GET /menu/productscategory_id (Kennung oder none), available, external_id, updated_sinceImmer aufsteigend nach updated_at.
GET /menu/categories, GET /menu/option-groups, GET /menu/optionsexternal_id, updated_sinceImmer aufsteigend nach updated_at.
GET /eventsafter (auch latest), types (auch menu.*)Aufsteigend, das älteste zuerst.

Ein Filter, den eine Liste nicht kennt, ergibt 400 invalid_request mit param. acknowledged bezieht sich auf deine App: acknowledged=false sind die Bestellungen, die deine App noch nicht übernommen hat, ohne zeitliche Grenze (siehe unten). Eine andere App sieht ihren eigenen Stand.

Muster: alles seit dem letzten Lauf

Ohne Webhooks ist updated_since der Weg. Merk dir den updated_at der letzten verarbeiteten Bestellung und frag damit regelmäßig nach; er steht in UTC und taugt unverändert als Wert (ein Zeitpunkt ohne Zeitzone ergibt 400). Mit updated_since kommen die Einträge aufsteigend, die älteste Änderung zuerst. Genauso geht es bei Reservierungen und den Listen der Karte, GET /menu/options eingeschlossen.

Nimm eine Minute Überlappung (updated_since eine Minute vor dem letzten Wert) und erkenne Doppelte an id und updated_at. Zwei Änderungen in derselben Sekunde können sonst zwischen zwei Abfragen durchrutschen.
// Everything that changed since the last run, oldest change first. listAll() as above.
const OVERLAP_MS = 60_000;

export async function pollOrders(accessToken, state) {
  const since = new Date(Date.parse(state.since) - OVERLAP_MS).toISOString();
  for await (const order of listAll(accessToken, "/orders", { updated_since: since })) {
    if (state.seen[order.id] === order.updated_at) continue; // handled in the overlap already
    await handleOrder(order); // your code; must tolerate seeing an order again
    state.seen[order.id] = order.updated_at;
    if (order.updated_at > state.since) state.since = order.updated_at;
  }
  // Forget what lies before the overlap, then persist the state for the next run
  for (const [id, at] of Object.entries(state.seen)) {
    if (Date.parse(at) < Date.parse(state.since) - OVERLAP_MS) delete state.seen[id];
  }
  await saveState(state);
}

Am sichersten ist beides zusammen: der Webhook ist schnell, die regelmäßige Abfrage holt nach, was verloren ging. Noch einfacher holt GET /events alles der Reihe nach.

Muster: nur, was noch nicht übernommen ist

Für eine Kasse ist die Frage selten „was hat sich geändert“, sondern „was habe ich noch nicht gebucht“. Genau das liefert GET /orders?acknowledged=false. Nach dem Buchen meldest du die Bestellung mit POST /orders/{id}/acknowledge als übernommen, mit deiner Belegnummer als external_id; danach fällt sie aus der Liste.

acknowledged=false hat keine zeitliche Grenze: die Liste enthält jede Bestellung, die deine App nie übernommen hat, auch solche von vor der Verbindung, abgeschlossene und stornierte. Grenz sie deshalb immer mit created_since ein, etwa mit dem created_at der Verbindung aus GET /me.

Das Muster verzeiht Abstürze: was nicht übernommen ist, kommt beim nächsten Lauf wieder. Dein Buchen muss dafür je Bestellung idempotent sein. Übernimm jede Bestellung, die du bearbeitet hast, auch eine stornierte, die du nicht buchst: sonst bleibt sie in der Liste und kommt bei jedem Lauf wieder.

# The orders your app has not taken over yet, since the connection was made
# (created_at from GET /me). Without created_since the list has no time limit.
curl "https://api.flavoso.com/v1/orders?acknowledged=false&created_since=2026-10-04T17:40:00.000Z&limit=50" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Booked into your system as receipt R-2026-000123: acknowledge it
curl -X POST https://api.flavoso.com/v1/orders/ORDER_ID/acknowledge \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ack-ORDER_ID" \
  -d '{"external_id": "R-2026-000123"}'

# Not booked, for example because it was cancelled already: acknowledge it all the same,
# otherwise it stays in the list
curl -X POST https://api.flavoso.com/v1/orders/OTHER_ORDER_ID/acknowledge \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: ack-OTHER_ORDER_ID"

Zeiträume

created_since und created_until grenzen Bestellungen nach ihrem Eingang ein, etwa für einen Tagesexport; starts_from und starts_until grenzen Reservierungen nach ihrem Beginn ein, etwa für das Reservierungsbuch eines Abends. Beides sind Zeitpunkte in UTC: der Tag des Betriebs beginnt um Mitternacht seiner Zeitzone, in Berlin im Sommer also um 22:00:00Z des Vortags. Der Anfang zählt mit, das Ende nicht: created_since und starts_from schließen den Zeitpunkt ein, created_until und starts_until schließen ihn aus. So schließt ein Tag lückenlos an den nächsten an.

Was nie in einer Liste steht

  • Unbezahlte Online-Bestellungen. Eine Bestellung mit Online-Zahlung gibt es für die Schnittstelle erst, wenn die Zahlung bestätigt ist, genau wie im Dashboard.
  • Gelöschte Reservierungen. Ihre Löschung meldet ein letztes reservation.updated.

Etwas unklar geblieben? Schreib an support@flavoso.com und nenne die Kennung aus X-Request-Id. Damit finden wir deine Anfrage im Protokoll.