← Learn

Migrate from HubSpot to Relm in one agent run

A HubSpot export plus a Relm API key is everything your AI agent needs. Point Claude, ChatGPT or any MCP client at the file and it recreates your pipeline, registers your properties as custom fields, and batch-loads every contact, company, deal and note - then verifies its own work.

CRM migrations used to mean a weekend with a column-matching wizard and a support thread about failed rows. With an API-first CRM the job changes shape: the agent reads your source, reads Relm's live schema, maps one to the other, and writes in batches. Relm was designed for exactly this pass - POST /v1/batch takes up to 100 writes per call, GET /v1/schema tells the model what exists, and every error comes back machine-readable with valid_options, so the agent fixes its own mistakes instead of stopping to ask you. This guide gives you the exact export mechanics, the field mapping, and the commands.

Why teams move off HubSpot

First, credit where it is due: HubSpot's free CRM is genuinely good, and the paid Hubs form a real all-in-one suite - marketing email, landing pages, forms, service desk, reporting. If your team lives in those tools, HubSpot is doing its job and you should stay.

The migration case is narrower and increasingly common: you use HubSpot as a contact and deal database, and the things reading and writing it are scripts and AI agents rather than people. Then the economics and the ergonomics both start to pinch. The free tier is the on-ramp to per-seat Sales Hub pricing and marketing contact tiers, and the automation you eventually want - programmable workflow actions, serious data sync and data quality tooling - sits in Operations Hub at the Professional tier. You end up paying for a suite while consuming a database.

The API side is a capability gap, not a quality complaint. HubSpot's CRM API is extensive, but it was built for integrations written by developers: properties live in their own registry, associations are a separate API, and nothing hands a language model the full contract in one call. Relm inverts that - one GET /v1/schema returns every object, field and enum value; IDs are prefixed (con_, cmp_, deal_, act_); errors are RFC-9457 problem+JSON; and the same key drives a native MCP server at https://api.relmcrm.com/mcp. The agent hub lists everything a model can discover on its own.

Step 1 - export your data from HubSpot

Two paths, both fine:

Practical split: CSVs are the easy path for the three core objects; use the API if you also want notes and call history, which do not come along in a simple index export.

Step 2 - the field mapping

HubSpot's API uses internal property names (firstname, dealstage); CSV headers use the display labels ("First Name", "Deal Stage"). The table below uses internal names - if you are mapping from a CSV, match the labels to the same rows.

HubSpotRelmNotes
Company: name, domaincompany.name, company.domainDedupe by domain on your side: create each company once, reuse its cmp_ id.
Company: industry, numberofemployeescompany.custom_fields.industry, .employee_countRegister once via POST /v1/fields (text, number).
Contact: firstname, lastname, email, phonefirst_name, last_name, email, phoneEmail is optional in Relm - phone-only or name-only contacts import fine.
Contact: jobtitle, hs_lead_statuscustom_fields.job_title, .lead_statusKeep HubSpot's raw values; they stay filterable.
Contact: lifecyclestagecontact.type + custom_fields.lifecycle_stagesubscriber through opportunity map to type lead; customer and evangelist map to customer. Store the exact stage in the custom field.
Contact: associated companycontact.company_idCreate companies first, keep a name-to-cmp_ map.
Deal: dealname, amount, closedatedeal.title, value_cents, close_dateHubSpot amounts are dollars; value_cents is integer cents - multiply by 100.
Deal: pipeline, dealstagedeal.pipeline, deal.stageRecreate each HubSpot pipeline with its stages 1:1 (below).
Notes, calls, emails, meetings (hs_note_body, hs_timestamp)activity with type, body, occurred_atActivity types: note, call, email, meeting, task. occurred_at preserves the original date.

Step 3 - one-time setup, in test mode

Mint a relm_test_ key first. Test mode is free, unlimited, and invisible to live data and billing - rehearse the whole migration there, then swap one string. Setup means recreating your pipeline and registering the HubSpot properties you are keeping. Re-running setup is safe: registering a field key that already exists is a no-op, and re-creating an existing pipeline key returns a 409 conflict instead of a duplicate.

# Recreate HubSpot's default sales pipeline, stages mapped 1:1
curl -X POST https://api.relmcrm.com/v1/pipelines \
  -H "Authorization: Bearer relm_test_..." -H "Content-Type: application/json" \
  -d '{"key":"sales_pipeline","name":"Sales Pipeline","stages":[
    {"key":"appointment_scheduled","label":"Appointment scheduled"},
    {"key":"qualified_to_buy","label":"Qualified to buy"},
    {"key":"presentation_scheduled","label":"Presentation scheduled"},
    {"key":"decision_maker_bought_in","label":"Decision maker bought-in"},
    {"key":"contract_sent","label":"Contract sent"},
    {"key":"closed_won","label":"Closed won"},
    {"key":"closed_lost","label":"Closed lost"}]}'

# Register HubSpot properties as custom fields (once per key)
curl -X POST https://api.relmcrm.com/v1/fields \
  -H "Authorization: Bearer relm_test_..." -H "Content-Type: application/json" \
  -d '{"object":"contact","key":"lifecycle_stage","data_type":"text"}'

Step 4 - batch-load in dependency order

Records get server-assigned IDs, so load in order across batches: companies → contacts → deals → activities. Within a single batch, operations cannot reference each other's new IDs. Each call takes up to 100 operations and returns a per-op result array - one bad row does not fail the batch, and every op counts against your quota exactly like an individual request. Batching saves round-trips, not quota.

curl -X POST https://api.relmcrm.com/v1/batch \
  -H "Authorization: Bearer relm_test_..." -H "Content-Type: application/json" \
  -d '{"operations":[
    {"object":"contact","data":{
      "email":"[email protected]","first_name":"Maria","last_name":"Diaz",
      "phone":"+1 512 555 0143","company_id":"cmp_8xk2...","type":"lead",
      "custom_fields":{"lifecycle_stage":"salesqualifiedlead",
        "job_title":"VP Operations","lead_status":"IN_PROGRESS"}}},
    {"object":"deal","data":{
      "title":"Acme Logistics - annual plan","value_cents":1850000,"currency":"usd",
      "pipeline":"sales_pipeline","stage":"contract_sent",
      "company_id":"cmp_8xk2...","close_date":"2026-08-15"}}
  ]}'

{ "object": "batch", "count": 2, "succeeded": 2, "failed": 0, "results": [...] }

Notes and calls follow the same pattern as activities, with occurred_at set to HubSpot's hs_timestamp so a note from 2023 still reads as a note from 2023. If the agent sends a stage or field Relm does not know, the 422 response lists valid_options and the exact call to create it - the self-correcting loop described in the docs.

Or let MCP do all of it

If your agent speaks MCP, skip the curl entirely. Connect it to https://api.relmcrm.com/mcp (config in how to give your AI agent a CRM) and hand it the export:

Import my HubSpot export into Relm. Files: contacts.csv, companies.csv, deals.csv.
Call relm_describe_schema first. Create a pipeline whose stages match my HubSpot
deal stages 1:1, register jobtitle / lifecyclestage / hs_lead_status as custom
fields, then load everything with relm_batch, 100 ops at a time, in the order
companies -> contacts -> deals. Amounts are dollars: convert to value_cents.
Skip nothing - contacts without an email are fine. Report counts when done.

The agent discovers the whole contract from relm_describe_schema and works through the plan without supervision. The flow is proven: a real import of 496 leads from a production CRM landed in Relm with zero errors.

Verify your data before you switch

Do not trust "done" - check it. Three passes, all cheap:

Re-runs are safe by design: a duplicate contact email returns a 409 conflict carrying the existing record instead of creating a twin, and the single-record create endpoints accept an Idempotency-Key header so a retried call replays the original result. When the test-mode rehearsal is clean, run the same pass with your relm_live_ key. For quota, count your writes: a portal with 5,000 contacts, 1,000 companies, 800 deals and 10,000 notes is roughly 17,000 operations - inside Pro ($29/mo, 100,000 requests). The Free plan's 1,000 requests a month suits a small book; Scale ($249/mo, 2,000,000) is for heavy agent traffic after the move. Plans are on the pricing page.

FAQ

How long does a HubSpot to Relm migration take?

Minutes, not weeks, for a typical portal. Batch calls carry 100 writes each, so a few thousand records land in a handful of round-trips, and the agent runs the whole pass - setup, load, verify - unattended.

Can my agent pull from the HubSpot API instead of a CSV export?

Yes. With a private app token it can read crm/v3/objects/contacts, companies and deals directly, plus engagements from the notes, calls, emails and meetings endpoints, and write to Relm in the same run. CSV needs no token and works just as well for the core objects.

What happens to my HubSpot custom properties and lifecycle stages?

Custom properties become typed custom fields: register each once with POST /v1/fields, send values under custom_fields. Deal stages map 1:1 onto a pipeline you create; lifecycle stage lives on as a custom field, with lead-ish stages mapped to contact type lead and customer or evangelist to customer.

Will re-running the import create duplicate contacts?

No. A duplicate email returns a 409 that includes the existing record, and the single-record create endpoints accept an Idempotency-Key header that makes a retry replay the original result instead of writing twice.

How much does migrating to Relm cost?

The rehearsal is free - test-mode writes are never metered. On live, each operation is one request: Free gives 1,000/mo with no card, Pro is $29/mo for 100,000, Scale is $249/mo for 2,000,000, with metered overage on paid plans.

Does Relm replace HubSpot's marketing tools?

No. Relm replaces the CRM layer - contacts, companies, deals, activities, pipelines, automations, webhooks and transactional email over REST and MCP. Landing pages, blogs, forms and campaigns are HubSpot suite features; if you rely on them, keep them and migrate only the CRM.

Ready to move?

Mint a free test key, hand your agent the export, and rehearse the whole migration without spending a request. See pricing.

Start free →