Lists and paging
Reelwire has two kinds of list, and they behave differently on purpose.
Logs never stop growing: /v1/jobs, /v1/posts, /v1/render-jobs, /v1/ingest-events,
/v1/approvals and /v1/deliveries. These are paged, searched and sorted by the server, over every
row the workspace has. The syndication lists are paged by the server too,
with a few differences of their own: see Other paged lists.
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
| Parameter | What it does |
|---|---|
page | Which page, from 1. Anything else is treated as 1. |
perPage | How many to a page. 1 to 200; outside that, the route's own default. |
limit | The older name for perPage, still honoured. |
q | What was typed, matched case-insensitively across the columns the row prints. At most 200 characters. |
sort | A 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, until | The 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:
| Route | sort |
|---|---|
/v1/jobs | createdAt. Anything else is the default, when the job last changed. |
/v1/posts | createdAt, state, platform, channelName |
/v1/render-jobs | submittedAt, state, templateId |
/v1/ingest-events | receivedAt, status, origin |
/v1/approvals | requestedAt, channelName |
/v1/deliveries | createdAt, updatedAt |
Every log answers with the page and the counts around it:
{
"jobs": [],
"total": 7,
"all": 7,
"page": 1,
"perPage": 1,
"facets": { "state": ["COMPLETED"] }
}
| Field | What it is |
|---|---|
total | Rows matching the filters. Divide by perPage for the last page. |
all | Rows in the window asked for, ignoring the other filters and the search, so you can say "7 of 412". |
page, perPage | What you actually got, after clamping. |
facets | The values each filter can usefully take. |
The default perPage differs per route, because the screens differ: 25 for /v1/jobs, 40 for
/v1/posts and /v1/deliveries, 30 for /v1/render-jobs, 50 for /v1/ingest-events, 200 for
/v1/approvals. Send perPage rather than relying on any of them.
/v1/jobs answers two counts more: stages, how many posts each stage holds, and unstaged, how
many the search and filters leave before a stage is chosen.
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. The exceptions are the three fixed choices
on /v1/jobs, stage, approval and archive, which list everything the filter takes.
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, /v1/render-jobs and /v1/deliveries 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 367 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
untilset 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.
Other paged lists
/v1/syndication/partners and /v1/syndication/deliveries take q, page, perPage and
ascending, and no sort and no window: they are always in the order the rows were made, newest
first. perPage is 50 by default and at most 500 for the participants, 200 for the deliveries, and
a larger number is cut to that most rather than ignored. The deliveries answer total, all (every
line the workspace has) and facets; the participants answer total and room, the allowance they
count against, and no facets. See Syndication.
The lists that are not paged
/v1/brands, /v1/channels, /v1/streams, /v1/queues, /v1/media, /v1/media/renders,
/v1/feeds, /v1/templates and /v1/syndication/groups return everything. page
and perPage on them do nothing.
A few are in between: lists that take limit alone, with no page.
| Route | limit |
|---|---|
/v1/direct-export/videos | Defaults to 24, at most 100. |
/v1/direct-export/queue | Not taken: every video still rendering or waiting, those that failed in the last two hours, and the 100 newest kept ones. |
/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.