Flavoso APIFundamentalsPagination and filters

Pagination and filters

Lists come in pages with a cursor. With filters, updated_since and acknowledged you reliably fetch only what is new.

Lists

Every list has the same shape: object: "list", the entries in data, plus has_more and next_cursor. limit picks 1 to 100 entries per page, the default is 25. For GET /events the cursor is called after and the default is 50.

If there is more, the response says has_more: true. Then pass next_cursor back as cursor until has_more is false. The cursor is opaque: pass it back, do not build it, do not store it.

The cursor does not carry the filters. Send the same filters on every page, otherwise you page through a different list.
# 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"

Filters

Separate multiple values of a filter with commas: status=pending,confirmed.

ListFiltersOrder
GET /ordersstatus, type, paid, acknowledged, updated_since, created_since, created_until, external_idMost recently changed first; with updated_since ascending by updated_at.
GET /reservationsstatus, updated_since, starts_from, starts_untilMost recently changed first; with starts_from or starts_until ascending by starts_at.
GET /menu/productscategory_id (an id or none), available, external_id, updated_sinceAlways ascending by updated_at.
GET /menu/categories, GET /menu/option-groups, GET /menu/optionsexternal_id, updated_sinceAlways ascending by updated_at.
GET /eventsafter (also latest), types (also menu.*)Ascending, the oldest first.

A filter the list does not support gets 400 invalid_request with param. acknowledged refers to your app: acknowledged=false returns the orders your app has not taken over yet, without any time limit (see below). Another app sees its own state.

Pattern: everything since the last run

Without webhooks, updated_since is the way. Remember the updated_at of the last order you processed and poll with it regularly; it is in UTC and can be sent back as it is (a timestamp without a time zone gets 400). With updated_since, entries come in ascending order, the oldest change first. The same works for reservations and the menu lists, GET /menu/options included.

Use a minute of overlap (updated_since one minute before the last value) and detect duplicates by id and updated_at. Otherwise two changes in the same second can slip between two polls.
// 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);
}

Both together are safest: the webhook is fast, the regular poll catches what got lost. Simpler still, GET /events delivers everything in order.

Pattern: only what has not been taken over yet

For a POS, the question is rarely “what changed” but “what have I not booked yet”. That is exactly what GET /orders?acknowledged=false returns. After booking, you mark the order as taken over with POST /orders/{id}/acknowledge, with your receipt number as external_id; after that it drops out of the list.

acknowledged=false has no time limit: the list holds every order your app never took over, including those from before the connection, completed and cancelled ones. So always limit it with created_since, for example with the connection's created_at from GET /me.

The pattern is crash-tolerant: anything not taken over comes back on the next run. For that, your booking has to be idempotent per order. Take over every order you handled, even a cancelled one you do not book: otherwise it stays in the list and comes back on every run.

# 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"

Time ranges

created_since and created_until limit orders by when they came in, for example for a daily export; starts_from and starts_until limit reservations by their start, for example for an evening's reservation book. Both are timestamps in UTC: the restaurant's day starts at midnight in its time zone, in Berlin in summer at 22:00:00Z of the previous day. The start counts, the end does not: created_since and starts_from include the instant, created_until and starts_until exclude it. So one day joins the next without a gap.

What is never in a list

  • Unpaid online orders. An order paid online exists for the API only once the payment is confirmed, exactly as in the dashboard.
  • Deleted reservations. Their deletion is reported by a final reservation.updated.

Still unclear? Write to support@flavoso.com and include the id from X-Request-Id. It lets us find your request in the log.