Concepts
Pagination and sync
updatedSince keeps another system in step.
Pages
{ "data": [...], "next": "<cursor>" }. Send ?cursor=<next> for the following page, until next is null. ?limit= is 1 to 200, 50 by default. A cursor that was not given by the API is refused 400 CURSOR_INVALID.
Filters
updatedSince, createdFrom, createdTo and externalId, and by its own fields — a document list by status, partnerId, issueFrom, issueTo and number, the contacts by type, search and active.
Keeping another system in step
updatedSince set a minute or two before it — a small overlap costs a few repeated rows and never misses one. Upsert by id.
let cursor = null;
do {
const url = new URL('https://app.kwn.tax/api/v1/invoices');
url.searchParams.set('limit', '200');
url.searchParams.set('updatedSince', lastSync); // an instant, UTC
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { 'X-Api-Key': process.env.KWN_API_KEY } });
const { data, next } = await res.json();
for (const invoice of data) upsert(invoice);
cursor = next;
} while (cursor);