Backfill and exit times
Re-send a past range of a service day after an error, and send when each party leaves so the live view can estimate the floor while your app is offline.
Updated · View as Markdown
Two additions in contract v1.7, for every app type. A backfill re-sends the history of a time range, so entries an error left out still reach the stored day and its replay. An exit time on a check-in says when that party leaves, so the live view can estimate the floor while your app is silent.
Backfill
POST https://floorplancraft.com/api/live/backfill, signed exactly as a push (see Signing requests) but with this path, so a push's signature never passes here. It shares the push's refusals: signature failures, 402, 403, 404, 413, 429 and 503.
{
"restaurant": "Example Play Centre",
"serviceDay": { "start": "2026-11-06T00:00:00+05:00", "end": "2026-11-07T00:00:00+05:00" },
"from": "2026-11-06T09:00:00+05:00",
"to": "2026-11-06T15:30:00+05:00",
"states": [
{ "at": "2026-11-06T09:00:00+05:00", "tables": [ { "label": "Soft play", "room": "Play hall", "capacity": 40, "state": "seated", "covers": 12, "openMinutes": null, "server": null } ], "stations": [] },
{ "at": "2026-11-06T09:07:12+05:00", "tables": [ { "label": "Soft play", "room": "Play hall", "capacity": 40, "state": "seated", "covers": 15, "openMinutes": null, "server": null } ], "stations": [] }
],
"events": [
{ "id": "checkin:T-58213", "at": "2026-11-06T09:03:41+05:00", "type": "seated", "label": "Soft play", "detail": "3 covers", "exitAt": "2026-11-06T10:03:41+05:00" }
]
}| Field | Rule |
|---|---|
serviceDay | As in a push: start before end, at most 36 hours. Its date in the venue's time zone is the day being replaced. |
from, to | The range to replace, inside the service day: start <= from < to <= end. A whole day is from = start, to = end. to must be at least 10 minutes in the past, and from within the last 30 days. |
states | 1 to 2,000 floors in increasing at, each inside the range, the first at from exactly. Each floor is checked as a push's floor is: tables, stations and the optional longSeatedMinutes. |
events | 0 to 2,000, each id once, each inside the range. exitAt as on a push. A label that names no table or station of the request's floors is blanked, not refused. |
- The body may be up to 2 MB. Each connection may send 6 backfills a minute, with bursts of up to 10; backfills and pushes have separate allowances.
- Send only the floors at changes: a floor is the whole state from its
atuntil the next one. - Everything stored for the range, live pushes included, is replaced by the request. Sending the same request twice gives the same result.
- A backfill is not a sign of life: it does not change the app's Last seen or its sequence.
- A day your app never pushed, within the last 30 days, is created and appears in the live view's Service day menu.
{ "accepted": true, "serviceDay": "2026-11-06", "states": { "stored": 41, "replaced": 37 }, "events": { "stored": 12, "replaced": 0 }, "unmatchedLabels": [] }| Answer | Meaning |
|---|---|
202 as above | Stored. A replay of that day can take up to a minute to show it. |
422 { "error": "invalid_backfill", "issues": [...] } | A rule was broken; up to 20 issues. Nothing was stored. |
422 { "error": "backfill_too_large", "limit": "states", "max": 2000, "needed": 2104 } | The day would pass one of its limits on stored floors, bytes or events (5,000 a day). Send fewer floors, or a day of more than 2,000 events as several ranges. Nothing was stored. |
When to send one
- Every night, the previous service day: it corrects anything an error lost without anyone noticing.
- After a known outage or a correction in your system, the range it affected.
Exit times
A check-in (seated or reservation_seated) may carry exitAt, in pushes and in backfills: when the party leaves, such as the end of the ticket's paid time. If they leave early, send the same event again, with the same id, and the real time.
- While your app is heard from (a push or heartbeat in the last 90 seconds), the live view shows the floors you send.
- Once it has been silent for 90 seconds, and the day has at least one check-in with
exitAt, the live view estimates each zone: your last count, less the people of earlier check-ins whoseexitAthas passed since. It never goes below 0; a zone being reset and a zone without a count are left as they were. - The people in a check-in are its
detail's number ("3 covers"or"3 guests"), else 1. - The viewer then reads “Estimated hh:mm from exit times” where it usually says “Refreshes every 10 s”. Replays of past days are never estimated.
- When your app is back, the heartbeat that ends the silence answers
"needFullState": true: send a feed push with the day's events.