Repull Migrate
TL;DR
Create a Connect session with purpose: "migrate". The property manager signs in to the system they use today; their properties, reservations, guests and conversations are copied into a workspace of their own. You read it with the endpoints you already use, adding one header: X-Workspace-Id.
Switching property management software is the hardest part of winning a new customer: their properties, upcoming reservations, guest history and message threads all live in someone else's system. Repull Migrate moves that data for you, so your onboarding can start with “connect your current PMS” instead of a spreadsheet.
How it works
- You create a migrate session for a property manager. Repull gives them a workspace of their own and returns its
workspaceId. - They open the hosted page (your logo, your wording), pick the system they use today and sign in to it.
- The import starts straight away. The hosted page shows the progress; you get a
migration.completedwebhook when it is done. - You read their data with the regular endpoints and
X-Workspace-Id. It stays up to date until you cut over.
Quickstart
1. Create the session:
curl https://api.repull.dev/v1/connect \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"purpose": "migrate",
"redirectUrl": "https://yourapp.com/onboarding/migrated",
"workspace": { "name": "Seaside Rentals", "externalRef": "acct_8812" },
"allowedProviders": ["guesty", "hostaway", "lodgify"],
"scope": ["listings", "reservations", "guests", "conversations"]
}'{
"sessionId": "cs_8gQrT2v9k3M4nLp7wJxYzAbC",
"url": "https://connect.repull.dev/cs_8gQrT2v9k3M4nLp7wJxYzAbC",
"expiresAt": "2026-09-24T22:20:44.455Z",
"purpose": "migrate",
"workspaceId": "1204"
}2. Send the property manager to url (a redirect or a popup). When they finish they come back to your redirectUrl with status=connected&workspaceId=1204; a popup also posts repull:connect:completed with the workspaceId to its opener.
3. Read their data once the import is in:
curl "https://api.repull.dev/v1/properties?limit=50" \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "X-Workspace-Id: 1204" curl "https://api.repull.dev/v1/reservations?limit=100" \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "X-Workspace-Id: 1204"
Workspaces and X-Workspace-Id
Every property manager you move gets their own workspace, created by yours. Their data never mixes with yours or with another property manager's. Your API key reaches any workspace your workspace created. Pass its id in X-Workspace-Id and every endpoint (properties, reservations, guests, conversations, availability) answers for that workspace. An id your workspace did not create is a 404.
Your own key, their data
What you control on the hosted page
| Field | What it does |
|---|---|
allowedProviders | Which systems the property manager can pick. Any PMS or channel (for example vrbo or airbnb for hosts who manage directly on a channel). Omit to show everything. |
copy | Your wording: title, subtitle, completedTitle, completedBody (up to 300 characters each). Anything you leave out uses Repull's migration wording, in the property manager's language. |
scope | What you want brought across, listed to the property manager before they connect: listings, reservations, guests, conversations, calendar, channelIds and more. |
| Branding | The same logo, colours and support links as your Connect pages (dashboard → Connect settings). |
locale | Pin the page language (en, fr); otherwise it follows the browser. |
Tracking a migration
GET /v1/migrations lists every property manager you have moved; GET /v1/migrations/{workspaceId} shows one: its state, the connected source with its last import, and how much has come across.
{
"data": {
"workspaceId": "1204",
"name": "Seaside Rentals",
"state": "imported",
"connections": [
{ "id": "812", "provider": "guesty", "status": "active",
"import": { "status": "completed", "finishedAt": "2026-09-24T18:06:47Z",
"results": [ { "entityType": "listings", "processed": 48, "errors": 0 } ] } }
],
"counts": { "listings": 48, "reservations": 1312, "upcomingReservations": 211, "guests": 1040, "conversations": 886 }
}
}| state | Meaning |
|---|---|
awaiting_connection | The property manager has not finished the hosted page yet. |
importing | The first import is running. |
imported | The data is in, and kept up to date until cutover. |
failed | The import stopped with an error (see connections[].import.error). Fix the cause and run it again. |
cut_over | The source was disconnected; the data stays readable. |
deactivated | The migration was ended with DELETE. |
Subscribe to migration.completed and migration.failedinstead of polling. Every event about a property manager's workspace (a new reservation, a guest message) reaches your webhooks too, carrying workspaceId so you know whose it is.
The migration report
GET /v1/migrations/{workspaceId}/reportlists what needs a decision on your side: properties without an address, upcoming stays with no guest contact, channel reservations you must not re-create (they come back through the channel), and what the source system cannot carry at all. It also includes the source's capability matrix.
Running the import again
The first import brings properties and reservations. Pull message history, or everything changed since a date, with:
curl -X POST https://api.repull.dev/v1/migrations/1204/import \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "entities": ["messages"] }'Channel connections do not move
Next
- Channel links and cutover — switching over without losing or double-booking a stay.
- What each system can bring across.
- Connect — the hosted flow Migrate is built on.