The message poll has its own envelope, with the flags at the top level
The three flags beside the data are what a polling client reads to decide whether to fetch again and whether the turn is over. Folding them into the shared pagination block would break every such client.
The rule
- The message poll answers
{success, data, hasMore, hasOlder, sessionStatus}in that order, with no pagination block and nohas_morekey: the three flags are siblings ofdata, not nested. hasMorereports page fullness (whether the page returned as many messages as were asked for) and is an inference.hasOlderis authoritative: it reports whether messages older than the page exist, so a client scrolling back never pays a speculative empty fetch at an exact page boundary.sessionStatusrides along so a poller needs no second request to learn the turn has finished.- The forward and backward cursors are honoured only when they are strings of digits; a cursor that is not is ignored, not refused.
What it means
The message poll does not reuse the shared paginated envelope: hasMore,
hasOlder and sessionStatus sit beside data, not nested inside a
pagination block, and there is no has_more key alongside them. A reader
used to the paginated shape elsewhere on this surface would look for the
flags in the wrong place.
The two boolean flags answer different questions and are not interchangeable.
hasMore only reports whether the page came back as full as it was asked to
be — an inference from the count, not a lookup. hasOlder is a real lookup:
it tells a client scrolling backward whether anything precedes the page it
already has, so it never has to make a speculative fetch just to find out the
history has run out. A cursor that is not a string of digits is silently
ignored rather than refused, so a poller sending a stale or malformed cursor
still gets a window back instead of an error.
Example
An empty session, polled with no cursor at all:
GET /api/flowdrop/playground/sessions/{session_id}/messages
{"success": true, "data": [], "hasMore": false, "hasOlder": false, "sessionStatus": …}Sending junk instead of a real cursor (since=abc&before=xyz) does not
refuse the request either — it falls back to the default window as if no
cursor had been sent.