Flambe docs
Guides

Syncing

Keep a local copy current with delta sync instead of refetching the library.

Refetching a whole library on every launch is wasteful and slow. GET /api/recipes supports a watermark so you can ask only for what changed.

Delta sync

Pass updated_since and you get back only recipes modified at or after that instant, plus the ids of recipes that are no longer visible:

curl "https://api.flambe.dev/api/recipes?updated_since=2026-09-09T00:00:00.000Z" \
  -H "Authorization: Bearer $FLAMBE_TOKEN"
200 OK
{
  "recipes": [ /* changed since the watermark */ ],
  "revoked_ids": ["3c2b1a09-8f7e-6d5c-4b3a-2918f7e6d5c4"],
  "server_timestamp": "2026-09-10T18:22:04.512Z",
  "full_sync_required": false,
  "next_cursor": null
}

Apply it like this:

  1. Upsert everything in recipes.
  2. Delete everything in revoked_ids — these are recipes deleted, or un-shared from a household.
  3. Store server_timestamp and send it as the next updated_since.

Use the server's timestamp, not your own clock. Clock skew between your device and the server will otherwise cause you to miss changes in the gap.

The 25-day cliff

Change history is retained for 30 days. A watermark older than 25 days is refused:

{
  "recipes": [],
  "revoked_ids": [],
  "full_sync_required": true,
  "server_timestamp": "2026-09-10T18:22:04.512Z"
}

The five-day margin exists so you are told to re-sync before the history you would need actually expires.

When you see full_sync_required: true, discard local state and re-sync without updated_since. Do not ignore it and do not retry with the same watermark — the response is empty, so a client that ignores the flag silently stops receiving updates forever.

async function sync(db: LocalStore) {
  const since = await db.getWatermark();
  const qs = since ? `?updated_since=${encodeURIComponent(since)}` : '';
  const res = await authedFetch(`/api/recipes${qs}`);
  const page = await res.json();

  if (page.full_sync_required) {
    await db.clear();
    await db.setWatermark(null);
    return sync(db); // once — the next call has no watermark, so it cannot recurse again
  }

  await db.upsertAll(page.recipes);
  await db.deleteAll(page.revoked_ids);
  await db.setWatermark(page.server_timestamp);
}

Pagination and delta sync are independent

limit and cursor work with or without updated_since. A large delta comes back paginated, so keep following next_cursor until it is null — and only then commit the new watermark. Committing it mid-page loses everything on the pages you have not read.

The change log

GET /api/recipes/changes is the lower-level primitive: an append-only event log, for when you need to know what happened rather than just the current state.

curl "https://api.flambe.dev/api/recipes/changes?since=0&limit=100" \
  -H "Authorization: Bearer $FLAMBE_TOKEN"

`since` here is a sequence number

Unlike updated_since on /api/recipes, the since on /changes is a numeric sequence watermark, not a timestamp. Start at 0 and carry forward the value the response gives you. Passing an ISO timestamp returns 400 Invalid since parameter.

Live updates

Delta sync covers catching up. For changes arriving while your client is open, subscribe to the event stream — and still delta-sync on reconnect, since events that happened while you were disconnected are not replayed.

On this page