The order
Load records before the things that point at them, so every link resolves on first pass.1
Members
Invite the people who own records in the old CRM. Owners are matched by email (
owner_email); an owner who is not a member leaves the record unowned, and a note or task keeps the author’s name as text.2
Structure
Pipelines and stages with
POST /api/v1/pipelines (stage kind decides open, won or lost, with a probability), custom fields with POST /api/v1/crm/fields. GET /api/v1/crm/schema shows what exists. The imports can also create text fields on the fly with create_missing_fields: true.3
Companies
POST /api/v1/companies/import, 1,000 rows per call, with domain and the source id as external_id.4
Contacts
POST /api/v1/contacts/import, 1,000 rows per call. company (the name) links each contact to the company imported above; created_at keeps the original creation date.5
Deals
POST /api/v1/deals/import, 1,000 rows per call, naming the pipeline and stage by name, the company by company_external_id and the contacts by contact_external_ids. A closed deal keeps its closed_at.6
History
POST /api/v1/activities/import, 500 rows per call: notes, emails, calls, meetings and tasks with their original occurred_at, attached by contact_external_id, company_external_id and deal_external_id.7
Do-not-contact
POST /api/v1/sequences/unsubscribes with up to 1,000 emails per call for everyone who opted out or bounced in the old system, so nobody is emailed by mistake.Ids and dates
Sendexternal_source once per call (for example "hubspot") and each row’s external_id. The id is stored with the record, and from then on any call can name the record by it: company_external_id on a deal, deal_external_id on an activity, contact_external_id anywhere a contact is linked. A row whose id is already known updates the record (companies also match on domain), so a migration can stop, resume and re-run safely; duplicate_mode: "skip" keeps what is already there.
created_at on contacts, companies, deals, notes and tasks, occurred_at on activities and closed_at on deals keep the original timestamps, so timelines and reports read the way they did before.
errors it left out, warnings for rows that imported with something missing (a contact that was never imported, say), and records: the Orbit id of each row.
Limits
A
429 carries Retry-After in seconds. An approved OAuth app has higher request limits (see rate limits).
What does not carry over
Form definitions, email templates, sequences and workflows are built in Orbit rather than imported; files can be attached withPOST /api/v1/files/import from a signed URL in the old system. Migrate from HubSpot with an agent maps every HubSpot object and engagement field to its Orbit counterpart.