Docs/Migrate/Repull Migrate

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

  1. You create a migrate session for a property manager. Repull gives them a workspace of their own and returns its workspaceId.
  2. They open the hosted page (your logo, your wording), pick the system they use today and sign in to it.
  3. The import starts straight away. The hosted page shows the progress; you get a migration.completed webhook when it is done.
  4. 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

You never handle the property manager's PMS credentials or a separate API key. The credentials they enter on the hosted page stay with Repull; you work with your own key plus the workspace id.

What you control on the hosted page

FieldWhat it does
allowedProvidersWhich 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.
copyYour 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.
scopeWhat you want brought across, listed to the property manager before they connect: listings, reservations, guests, conversations, calendar, channelIds and more.
BrandingThe same logo, colours and support links as your Connect pages (dashboard → Connect settings).
localePin 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 }
  }
}
stateMeaning
awaiting_connectionThe property manager has not finished the hosted page yet.
importingThe first import is running.
importedThe data is in, and kept up to date until cutover.
failedThe import stopped with an error (see connections[].import.error). Fix the cause and run it again.
cut_overThe source was disconnected; the data stays readable.
deactivatedThe 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

An Airbnb, Booking.com or VRBO connection belongs to the PMS it was made with — no API can hand it to another. The property manager reconnects the channels in your product. Use the channel map to link each channel listing to the right property automatically when they do.

Next

AI