Skip to content
Linra Omni Developer Docs

Catalog delta sync

Efficiently keep your own copy of the scent catalog in sync with changedSince.

If you mirror the scent catalog (scents, brands, variants) into your own system, don’t re-fetch the entire catalog on every sync — use the changedSince query parameter, supported identically on all three list endpoints:

  • GET /api/v1/scent/scents?changedSince=...
  • GET /api/v1/scent/brands?changedSince=...
  • GET /api/v1/scent/variants?changedSince=...

changedSince takes an ISO 8601 timestamp and returns only rows changed strictly after that instant. A value without an offset is read as UTC, and a date-only value (2026-08-01) means 2026-08-01T00:00:00Z; a value that doesn’t parse returns 400 VALIDATION_INVALID_DATE (it is never silently ignored, so a typo can’t turn a delta sync into a full download). When set, results are ordered by change time ascending (oldest-changed first) instead of the endpoint’s normal default ordering, so you can page through a large batch of changes in a stable order.

Terminal window
GET /api/v1/scent/variants?changedSince=2026-08-01T00:00:00Z&pageSize=100

The partner catalog rows don’t carry a change timestamp of their own, so your cursor is the time you started the previous sync, not a value read from the rows.

  1. On first sync, note the current UTC time, then omit changedSince entirely and page through the full catalog. The time you noted is your cursor.
  2. On every subsequent sync, note the current UTC time again, then call the same endpoint with changedSince set to your previous cursor — minus a few seconds of overlap, so a row committed while your last sync was running is never missed. Re-applying a row you already have is harmless if your local write is an upsert by id.
  3. Page through until you get a partial or empty page (page >= pagination.totalPages), keeping the same changedSince on every page — the endpoint does not advance your cursor for you.
  4. When the whole run succeeded, store the time you noted in step 2 as the new cursor.
async function syncSince(cursor) {
const startedAt = new Date();
const since = new Date(cursor.getTime() - 5_000); // 5 s of overlap
let page = 1;
while (true) {
const res = await client.get('/api/v1/scent/variants', {
params: { changedSince: since.toISOString(), page, pageSize: 100 },
});
const { items, pagination } = res.data.payload;
for (const item of items) await upsertLocalVariant(item); // keyed by item.id
if (page >= pagination.totalPages) break;
page++;
}
return startedAt; // the next cursor
}

pageSize accepts 1 to 100 (default 20); anything else returns 400 VALIDATION_INVALID_PAGE_SIZE.