This guide explains how to keep a local copy of an organization's bookings in sync with SERVOS.
Why not just poll orders by date?
GET /admin/v0/orders filters by when the order reached SERVOS (created_at), and the order's updated_at
does not change for every relevant transition. In particular, check-in and
no-show are recorded on the booking item, not the order, so an order you
already fetched can change status without its updated_at moving.
Polling orders by created_from therefore finds new bookings but misses
updates to existing ones. Use the events feed for updates.
Recommended loop
- Backfill once: page through
GET /admin/v0/orders(optionally withcreated_from) to load history. - Store a cursor: remember the
occurred_atof the last event you processed (start it at your backfill time). - Poll
GET /admin/v0/events?since=<cursor>on an interval. Events come oldest-first. - For each page, collect the distinct
order_idvalues, then re-fetch each withGET /admin/v0/orders/{order_id}to get its current state. - Advance your cursor to the
occurred_atof the last event processed.
A booking's end emits no event: its status and its order's
fulfillment_status change when ends_at passes, so also re-fetch the orders
of bookings that have ended since your last sync.
cursor = last_synced_at
loop:
events = GET /admin/v0/events?since=cursor (page through all pages)
for order_id in distinct(events[].order_id):
order = GET /admin/v0/orders/{order_id}
upsert(order)
cursor = max(events[].occurred_at) # if any
sleep(interval)Cursor tips
- Use
occurred_atas the cursor, and de-duplicate by eventid, two events can share a timestamp, so re-process the boundary timestamp idempotently rather than assuming strict greater-than. - Re-fetching the order (rather than trusting the event payload) means you always store the order's authoritative current state, including any changes you might have missed.
Customers
Customer records sync separately and more simply: poll
GET /admin/v0/customers?updated_since=<cursor>. Customer edits and
marketing consent given or withdrawn bump updated_at, so this is reliable on
its own. (Remember: customers are
pseudonymous unless your key has customer_data:read.)