Skip to main content

Lists and paging

Reelwire has two kinds of list, and they behave differently on purpose.

Logs never stop growing: posts, jobs, renders, ingest events, approvals, invoices. These are paged, searched and sorted by the server, over every row the workspace has.

Configuration does not grow without bound: brands, channels, streams, post lists, the media library. These come back whole, and the client searches and sorts them itself. A library is hundreds of files, not millions, and paging it would buy nothing and cost a round trip.

The log parameters​

ParameterWhat it does
pageWhich page, from 1. Anything else is treated as 1.
perPageHow many to a page. 1 to 200; outside that, the route's own default.
limitThe older name for perPage, still honoured.
qWhat was typed, matched case-insensitively across the columns the row prints. At most 200 characters.
sortA column name the route offers. Anything else falls back to the route's default order.
ascending"true" or "false". Absent is the route's own default: newest first, except on /v1/approvals, which is a queue and is read oldest first.
since, untilThe window, as absolute instants.
curl "http://localhost:4000/v1/render-jobs?page=2&perPage=25&q=bund&sort=state&ascending=true" \
-H "Authorization: Bearer rw_your_key_here"

The columns each log can be sorted by:

Routesort
/v1/jobscreatedAt. Anything else is the default, when the job last changed.
/v1/postscreatedAt, state, platform, channelName
/v1/render-jobssubmittedAt, state, templateId
/v1/ingest-eventsreceivedAt, status, origin
/v1/approvalsrequestedAt, channelName

Every log answers with the page and the counts around it:

{
"jobs": [],
"total": 7,
"all": 7,
"page": 1,
"perPage": 1,
"facets": { "state": ["COMPLETED"] }
}
FieldWhat it is
totalRows matching the filters. Divide by perPage for the last page.
allRows in the window asked for, ignoring the other filters and the search, so you can say "7 of 412".
page, perPageWhat you actually got, after clamping.
facetsThe values each filter can usefully take.

The default perPage differs per route, because the screens differ: 25 for /v1/jobs, 40 for /v1/posts, 30 for /v1/render-jobs, 50 for /v1/ingest-events, 200 for /v1/approvals. Send perPage rather than relying on any of them.

Facets are read from the rows​

facets is computed from what the workspace actually holds, not from a fixed list. A filter value that appears there is one that can return something, and a state nothing is in does not appear. Some lists also carry labels, which maps ids in the facets to names, so a filter dropdown does not need a second request to say "Channel 4 de" instead of a ULID.

An unparseable filter widens rather than refuses​

since and until that cannot be read are ignored, not rejected. This is a filter on a list, and the honest failure is to show more, not to show an error. The same goes for a sort column a route does not offer, and for a filter repeated in the query string: a key given twice arrives as an array, a filter is only ever one value, so it is treated as no filter at all.

A search longer than 200 characters is the one exception, and is a 400.

Windows mean different things​

On /v1/jobs, the window is when something happened to the job: it arrived, it rendered, or one of its posts changed. A job that arrived yesterday and published ten minutes ago is inside the last hour, because the rows sort by Updated and anything else would be a lie.

On /v1/posts and /v1/render-jobs it is when the row was created, and on /v1/ingest-events when the event arrived. /v1/approvals takes no window: it is the queue of what waits now.

/v1/calendar is not a log and takes from and to instead, both required, both absolute instants, with a window of at most 62 days.

Paging safely while things are arriving​

A log is ordered newest first, so a row arriving between two requests shifts everything down a place and page 2 can repeat a row page 1 already showed. Two ways round it:

  • Pin the window. Pass until set to the moment you started, and page through a set that cannot grow underneath you.
  • Page from the oldest end, with ascending=true, where you are reading everything rather than the most recent.

Every row carries an id, so deduplicating on the way through works whatever you do.

The lists that are not paged​

/v1/brands, /v1/channels, /v1/streams, /v1/queues, /v1/media, /v1/media/renders, /v1/feeds and /v1/templates return everything. page and perPage on them do nothing.

A few are in between: logs that take limit alone, with no page.

Routelimit
/v1/deliveriesDefaults to 40, at most 200.
/v1/direct-export/videosDefaults to 24, at most 100.
/v1/ai/chatsNot taken: the 50 most recent chats of the last day.

/v1/pipeline is a live view rather than a log: it takes since and until, and shows at most 12 jobs in each lane beside counts that cover them all. See Posts and jobs.