Keep item codes in sync with the change feed
Poll the change feed with a cursor, or take webhooks, so a correction or a withdrawal reaches your item codes inside the 24 hours the terms allow.
This is a worked example, not a supported connector. Interchange does not build or run connectors. You own the code you write from it, and the support boundary applies. Read the support boundary.
This recipe needs an API key on a plan that includes the endpoint. Read the key from an environment variable and never put it in a web page.
How it works
Start with
since, then keep the cursorYour first call sends
since. Every response has anext_cursor: save it and send it ascursoron the next call, withoutsince. An empty page means you have caught up, and the cursor is still where to resume.Apply a page, then save its cursor
Save the cursor only after you have applied the page. If you stop part-way, the next run repeats a page, and your handler must cope with that.
Be idempotent
Use
event_idto ignore an event you have handled. Webhook retries and re-read pages deliver the same event again.Treat a withdrawal as urgent
A withdrawn item must stop being shown. Hide it first, then relink it to
replaced_byif one is given.Webhooks are the same events, pushed
If you take webhooks instead, verify the signature and the timestamp first (see the reference), then run the same handler. Keep polling as a safety net.
The code
Node 22, no libraries. Adapt it to your language and your storage.
const BASE = "https://api.interchangeparts.com.au";
const KEY = process.env.INTERCHANGE_API_KEY; // only the keyed endpoints need it
async function call(path, init = {}) {
const response = await fetch(BASE + path, {
...init,
headers: { Accept: "application/json", ...(KEY ? { Authorization: "Bearer " + KEY } : {}), ...init.headers },
});
// Errors are application/problem+json, success is application/json. Both parse as JSON.
return { status: response.status, body: await response.json(), requestId: response.headers.get("Interchange-Request-Id") };
}
// store is yours: anything that can save and load one string and one set of handled event IDs.
export async function syncChanges(store, apply) {
let cursor = await store.loadCursor();
const first = cursor ? "cursor=" + encodeURIComponent(cursor) : "since=" + encodeURIComponent(await store.loadStart());
let query = first;
for (;;) {
const { status, body, requestId } = await call("/v1/changes?limit=25&" + query);
if (status === 429) throw new Error("Rate limited: wait for Retry-After, then run again");
if (status !== 200) throw new Error(body.code + " (request " + requestId + ")");
for (const change of body.changes) {
// The same event can arrive twice (a webhook retry, or a page re-read), so ignore one already handled.
if (await store.hasHandled(change.event_id)) continue;
await apply(change);
await store.markHandled(change.event_id);
}
// Save the cursor only after the page is applied. An empty page means you have caught up, and the cursor is still
// where to resume from.
await store.saveCursor(body.next_cursor);
if (body.changes.length === 0) return;
query = "cursor=" + encodeURIComponent(body.next_cursor);
}
}
// What to do with each kind of change. Treat a type you do not know as "look the item up again".
export async function applyChange(change, products) {
switch (change.type) {
case "item.withdrawn":
await products.hide(change.ix_id); // stop showing it now
if (change.replaced_by) await products.relink(change.ix_id, change.replaced_by);
break;
case "item.retired":
if (change.replaced_by) await products.relink(change.ix_id, change.replaced_by);
break;
default:
await products.refresh(change.ix_id); // published, updated, or a type added since you wrote this
}
}
Watch for
- A cached answer may be kept for 12 hours at most, and a correction or withdrawal must reach what you show within 24 hours. Poll at least hourly.
- Treat a change type you do not know as a reason to look the item up again. Types may be added within a version.
- The change feed needs an API key on a plan that includes it.
The calls are described in the API reference.