Polling
Polling keeps your system right even if a webhook is missed.
The pattern
- First run:
GET /v1/leads?since=2000-01-01T00:00:00Z. Follownext_cursorwhilehas_moreistrue. That loads every lead, oldest change first. - Save
next_sincefrom the last page. - Every 5 to 15 minutes:
GET /v1/leads?since=<saved next_since>, followingnext_cursorthe same way. - For each lead, insert it or update your copy, matched on
id. Skip it if your copy has the same or newerupdated_at. - Save the new
next_since.
const BASE = 'https://zqawbbylldymdfzineqq.supabase.co/functions/v1/pro-api/v1/leads';
async function sync(store, key) {
let since = (await store.get('since')) || '2000-01-01T00:00:00Z';
let cursor = null;
do {
const url = new URL(BASE);
if (cursor) url.searchParams.set('cursor', cursor); else url.searchParams.set('since', since);
const r = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
if (r.status === 429) return; // try again next run
if (!r.ok) throw new Error(`leads ${r.status}`);
const page = await r.json();
for (const lead of page.leads) await upsertById(lead);
cursor = page.has_more ? page.next_cursor : null;
if (page.next_since) since = page.next_since;
} while (cursor);
await store.set('since', since);
}Notes
- A change being saved at the very moment you read can carry a time a moment earlier than
next_since. If you want to be certain, sendsincea minute or two earlier than you saved; matching onidandupdated_athandles the repeats. - Messages, charges and reviews have their own lists (
sinceor date filters) and webhook events. - Every 5 minutes is 12 calls an hour; your limit is 600 (see limits).