Imports: knowing when a sync is done
When an account is connected, Repull imports what it already holds: listings, reservations, conversations and reviews. Every import is tracked from start to finish, so you can tell your users exactly when their data is ready instead of guessing.
When an import runs
connect— the first sync, right after the account is connected.mapping— a room or unit was mapped to a listing after connecting, so its bookings and messages import now.reconnect— the account was connected again after a disconnection.resync— a later full re-sync.
Wait for it
With webhooks (recommended). Subscribe to account.import.started, account.import.completed and account.import.failed. Each carries the standard account block (provider, externalAccountId), so you know which of your users it is about.
{
"event": "account.import.completed",
"eventId": "3f1c9a2e-8b7d-4c6a-9e0f-1a2b3c4d5e6f",
"account": { "id": "228", "provider": "booking_extranet", "externalAccountId": "83" },
"data": {
"importId": "7f0c2a9e-3b1d-4c55-9a8e-2d6f1b0c4e71",
"trigger": "connect",
"status": "completed",
"outcome": "complete",
"startedAt": "2026-10-16T18:24:19.000Z",
"completedAt": "2026-10-16T18:29:23.000Z",
"stages": [
{ "name": "account", "expected": 1, "done": 1, "failed": 0 },
{ "name": "reservations", "expected": 9, "done": 9, "failed": 0 },
{ "name": "reviews", "expected": 1, "done": 1, "failed": 0 }
],
"counts": { "listings": 1, "reservations": 219, "reviews": 100 }
}
}By polling. Every connection on GET /v1/connect carries its latest import, and GET /v1/connections/{id}/imports lists the account's runs, newest first (id is {provider}:{externalAccountId}).
// Wait until the account's newest import has finished.
// handle = '{provider}:{externalAccountId}', e.g. 'booking_extranet:36'
async function waitForImport(handle: string) {
for (;;) {
const res = await fetch(`https://api.repull.dev/v1/connections/${handle}/imports?limit=1`, {
headers: { Authorization: `Bearer ${process.env.REPULL_API_KEY}` },
})
const [run] = (await res.json()).data
if (run && run.status !== 'running') return run // 'completed' or 'failed'
await new Promise((r) => setTimeout(r, 30_000))
}
}Status, outcome, reason
running— still going.stagesshows progress: a stage is done whendone + failedreachesexpected(expectedisnulluntil the stage knows how much work it has).completedwithoutcome: complete— everything imported.completedwithoutcome: partial— it finished, but some units failed (see each stage'sfailed). What did import is readable.failed— it stopped.reasonsays why:needs_permissions— the host must grant more access. The connection onGET /v1/connectcarries afixUrl; once the host fixes it, a new import starts.duplicate_account— the same account is already connected in this workspace.stalled— no progress for too long. Reconnect or re-sync the account.
Readable a moment after completed
account.import.completed fires when every stage has fetched its data. The last records can take a minute or two to finish saving, so a count read straight after the event may still be climbing.History is read, not announced
An import does not announce every past booking as a reservation.created webhook: webhooks are for what changes next. Read the history with GET /v1/reservations, GET /v1/conversations and GET /v1/reviews once the import has completed.
Stages by channel
| Channel | Stages |
|---|---|
| Airbnb | listings, reservations, then messages, reviews and transactions when requested |
| Booking.com (connectivity provider) | reservations, after a room is mapped |
| Booking.com Extranet | account, enrich (rooms), reservations, conversations, reviews. Reservations import for mapped rooms; a connection with nothing mapped completes with none, and mapping a room later starts a mapping import. |
| Vrbo | priority (upcoming bookings and the last 30 days), then history |
| PMS (Hostaway, Guesty, …) | one stage per entity: listings, reservations, messages, calendar, reviews, payments |
Related: Webhook event types, Connect, List connections.