API de FlavosoFundamentosPaginación y filtros

Paginación y filtros

Las listas llegan por páginas con cursor. Con filtros, updated_since y acknowledged obtienes de forma fiable solo lo nuevo.

Listas

Todas las listas tienen la misma forma: object: "list", las entradas en data, además de has_more y next_cursor. limit fija entre 1 y 100 entradas por página; el valor por defecto es 25. En GET /events, el cursor se llama after y el valor por defecto es 50.

Si hay más entradas, la respuesta incluye has_more: true. Entonces envías next_cursor como cursor hasta que has_more sea falso. El cursor es opaco: devuélvelo tal cual, no lo construyas tú y no lo guardes.

El cursor no lleva consigo los filtros. Envía los mismos filtros en cada página; si no, estarás paginando otra lista.
# 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"

Filtros

Si un filtro lleva varios valores, sepáralos con comas: status=pending,confirmed.

ListaFiltrosOrden
GET /ordersstatus, type, paid, acknowledged, updated_since, created_since, created_until, external_idPrimero lo modificado más recientemente; con updated_since, ascendente por updated_at.
GET /reservationsstatus, updated_since, starts_from, starts_untilPrimero lo modificado más recientemente; con starts_from o starts_until, ascendente por starts_at.
GET /menu/productscategory_id (identificador o none), available, external_id, updated_sinceSiempre ascendente por updated_at.
GET /menu/categories, GET /menu/option-groups, GET /menu/optionsexternal_id, updated_sinceSiempre ascendente por updated_at.
GET /eventsafter (también latest), types (también menu.*)Ascendente, el más antiguo primero.

Un filtro que la lista no admite da 400 invalid_request con param. acknowledged se refiere a tu app: acknowledged=false devuelve los pedidos de los que tu app todavía no se ha hecho cargo, sin límite de tiempo (consulta más abajo). Otra app ve su propio estado.

Patrón: todo desde la última ejecución

Sin webhooks, la solución es updated_since. Guarda el updated_at del último pedido procesado y consulta con él periódicamente; está en UTC y puedes enviarlo tal cual (una fecha sin zona horaria da 400). Con updated_since, las entradas llegan en orden ascendente, el cambio más antiguo primero. Lo mismo vale para las reservas y las listas de la carta, GET /menu/options incluida.

Usa un minuto de solapamiento (updated_since un minuto antes del último valor) y reconoce los duplicados por id y updated_at. Si no, dos cambios en el mismo segundo pueden colarse entre dos consultas.
// 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);
}

Lo más seguro es combinar ambos: el webhook es rápido y la consulta periódica recupera lo que se haya perdido. Aún más sencillo: GET /events lo recoge todo en orden.

Patrón: solo los pedidos de los que aún no te has hecho cargo

Para una caja, la pregunta rara vez es «qué ha cambiado», sino «qué no he registrado todavía». Eso es justo lo que devuelve GET /orders?acknowledged=false. Tras registrar el pedido, confirmas con POST /orders/{id}/acknowledge que te has hecho cargo de él, con tu número de ticket como external_id; después desaparece de la lista.

acknowledged=false no tiene límite de tiempo: la lista contiene todos los pedidos de los que tu app nunca se ha hecho cargo, incluidos los anteriores a la conexión, los terminados y los cancelados. Por eso, acótala siempre con created_since, por ejemplo con el created_at de la conexión de GET /me.

El patrón tolera los fallos: los pedidos de los que no te has hecho cargo vuelven en la siguiente ejecución. Para ello, el registro en tu caja tiene que ser idempotente por pedido. Hazte cargo de cada pedido que hayas tratado, incluso de uno cancelado que no registres: si no, se queda en la lista y vuelve en cada ejecución.

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

Intervalos de tiempo

created_since y created_until acotan los pedidos por su hora de entrada, por ejemplo para una exportación diaria; starts_from y starts_until acotan las reservas por su hora de inicio, por ejemplo para el libro de reservas de una noche. Ambos son instantes en UTC: el día del negocio empieza a medianoche en su zona horaria; en Berlín, en verano, a las 22:00:00Z del día anterior. El inicio cuenta y el final no: created_since y starts_from incluyen el instante; created_until y starts_until lo excluyen. Así, un día enlaza con el siguiente sin huecos.

Lo que nunca aparece en una lista

  • Los pedidos online sin pagar. Un pedido con pago online solo existe para la API cuando se confirma el pago, igual que en el panel.
  • Las reservas borradas. Su borrado se comunica con un último reservation.updated.

¿Algo no ha quedado claro? Escribe a support@flavoso.com e indica el identificador de X-Request-Id. Así podremos encontrar tu solicitud en el registro.