Compare · AI assistants

Can ChatGPT import data into my CRM? Here's what's missing.

Partly, yes. An assistant such as ChatGPT, Claude or Copilot can clean a file, and with an MCP connection to your CRM it can create records one at a time. What it does not give you is a way to check the whole import before it writes, a cheap way to apply the same fix to every row, or a way to do it again next month.

Start freeHow Sloose works with your assistant

Clean an Excel file with ChatGPT or Copilot, or review an import

Ask an assistant to tidy a spreadsheet and it hands you a new file. It may be right. To know, you check it by hand, row by row, because what it changed is in the new file and nowhere else.

In Sloose the AI writes a rule for each column instead: split the full name, read the date, turn "NSW" into "New South Wales". You can read every rule, you see its output on your own rows, and Validate checks the whole import before anything is written.

AI mapping that fixes the values

A column card in Sloose: a formula that splits a full name into first and last names, with its output for each row.
The AI writes one formula per column. You see what it makes of every row before anything is written. Shown with Zoho CRM.

Bulk preview is cheap: one rule per column, run on every row

An assistant that writes records one at a time reads and writes each record, so a longer file means a longer, slower conversation. Sloose asks the AI about the columns and values once. The rules it writes run in your browser on every row, as many times as you like, with no AI cost.

Priced by the question, not the rows

An AI ask is priced by what it is about, such as how many columns or values. A file of 50 rows and one of 50,000 with the same columns cost the same to map their columns.

Running is free of AI

Runs, dry runs and retries never use AI credits. Rows count against your plan, nothing else.

The same rule every time

A rule does not change between runs unless you change it. Run the import again tomorrow and it does the same thing.

Dry-run results in Sloose: each row marked Would create or Exists for Accounts, Contacts and Deals, with nothing written to the CRM.
A dry run writes nothing. It shows what each row would create, and what it would find already there. Shown with Zoho CRM.

Links between records need IDs

Zoho's API takes a lookup as the related record's ID. So before an assistant writing through the API can link a contact to its account, it has to find the account's ID, for every row.

Sloose finds the related record by a field you choose, such as a name or an email, where your CRM can search it, and says what to do when nothing matches or several records do. A record made earlier in the same run is found from the run itself, not from a search that may not see it yet. Lookups by search, in the docs

Zoho's insert records API and MCP overview, checked 3 October 2026.

Matching existing records, retries and history

A bulk import has to decide, row by row, whether a record already exists. In Sloose each module has its own match rule, and when a row matches more than one record, Sloose can stop and ask you which one.

Every run keeps a results file. When some rows fail, a retry skips the records the first run made, so fixing the failures does not create the successes twice.

Matching existing records ·Results and retries, in the docs

A dialog during an import asking which of two matching contacts to update, or whether to skip or create.
When a row matches more than one record, you choose, record by record. Shown with Zoho CRM.

Repeatable: a template, not a new chat every month

Save the import as a template. When next month's file arrives, Sloose checks it against the template for free and shows what changed: a renamed column, a new value. You decide only what is new.Templates

AI assistants compared with Sloose for a CRM import

An AI assistant and Sloose, for a bulk import
ComparedAn AI assistant with MCPSlooseBetter
Cleaning a messy fileIt can rewrite the file, or write code that does, and hand you a new file to trust.One rule per column, which you can read, with its output shown on your rows.Sloose for a file you will import
Review before anything is writtenOpenAI helpEach record is a tool call. ChatGPT may ask you to confirm a write, one call at a time.The whole import at once: Validate, then a dry run that writes nothing.Sloose
Cost as the file growsThe model reads and writes each record, so more rows means a longer conversation.The AI is asked about the columns and values. Its rules run on every row with no AI cost.Sloose
Links between recordsZoho APIZoho’s API takes a lookup as the related record’s ID, so each ID has to be found first.Find the related record by a field you choose. A record made earlier in the same run is found from the run.Sloose
Records you already haveWhatever you ask for in the prompt, decided again in each conversation.A match rule for each module, and a choice for one match and for several, up to asking you record by record.Sloose
Next month’s fileA new conversation.A template, checked against the new file for free.Sloose
History and retriesThe chat transcript.A results file for every run. A retry skips the records the first run made.Sloose
One-off questions, PDFs, emails, other systemsYes. This is what assistants are good at.No. Sloose imports spreadsheets and text files into your CRM.An AI assistant with MCP
PriceOften part of a subscription you already pay for.A free plan. Paid plans from US$49 a month. AI asks use credits.An AI assistant with MCP

Facts about other products last checked on 3 October 2026.

Where an assistant is better

  • You may already pay for it. Many teams have ChatGPT, Claude or Copilot already.
  • It reads anything. A PDF, an email, a screenshot. Sloose reads spreadsheets and text files.
  • It works across systems and answers one-off questions in plain words.
  • It can write to your CRM today. Zoho publishes an MCP server that can create and update records, and HubSpot's remote MCP server gained write access when it became generally available on 13 April 2026.

Sources: Zoho, HubSpot, checked 3 October 2026.

Sloose works with your assistant, through MCP

You do not have to choose. Your assistant can use Sloose's MCP tools to read your CRM's fields, propose an import and save it as a template. A person then reviews it and runs it in Sloose.

From a conversation to a reviewed import

  1. Connect and consent

    Point an MCP client, such as Claude, at Sloose. You sign in, choose the organisation it may use, and allow it. This is OAuth 2.1, and the assistant works with the role you hold.

  2. Read the modules and fields

    list_modules and get_fields read what your CRM holds: each module, and the fields an import can write, with their picklist options.

  3. Propose the matching and the mapping

    propose_matching suggests how each module recognises a record you already have. propose_mapping suggests a field for each column. These two tools use AI credits. Every proposal is checked against your CRM’s real fields.

  4. Test the expressions

    test_expression runs a rule on sample rows, so the assistant sees what it produces before it saves anything.

  5. Validate the template

    validate_template checks the recipe against your CRM: fields that do not exist, rules that do not compile, and values that are not one of a picklist’s options.

  6. Save the template

    save_template stores the recipe in your organisation. Nothing has been written to your CRM.

  7. You open an import from the template in Sloose, check it, dry-run it and run it. No MCP tool runs an import.

A real session with claude-sonnet-5-5, recorded 3 October 2026.

  1. You

    I have a spreadsheet of leads from an expo and want it in Zoho CRM as an account, a contact and a deal for each row. Please work out how it should be imported using the Sloose tools, check it, and save it as a template called "Expo leads". The Sloose connection to use is 01a100aa-c5ae-7797-81ad-7cb6addb0f6d. Here is the file: Company,Contact,Job title,Email,Mobile,Opportunity,Value,Stage,Expected close Brightwater Joinery Pty Ltd,Priya Ramanathan,Operations Manager,priya.r@brightwater.example,0491 570 156,Workshop fit-out,"$48,500",Quote sent,15/11/2026 Brightwater Joinery Pty Ltd,Tom Okafor,Purchasing,tom@brightwater.example,0491 570 157,Spare parts contract,"$6,200",Qualified,30/11/2026 Kestrel Ridge Hire,Lena Fischer,Owner,lena@kestrelridge.example,0491 570 158,Fleet tracking pilot,"$12,000",Discovery,1/12/2026 Saltbush Solar Co.,Marcus Bell,Project Lead,m.bell@saltbush.example,0491 570 159,Warehouse roof array,"$96,750",Negotiation,20/12/2026 saltbush solar co,Aisha Rahman,Finance,aisha.rahman@saltbush.example,0491 570 110,Warehouse roof array,"$96,750",Negotiation,20/12/2026 Tidewell Plumbing,Sam Nguyen,Director,sam@tidewell.example,0491 571 266,Service agreement,"9,600",quote sent,2026-12-05 Orchard Lane Catering,Grace Liu,Events Coordinator,grace@orchardlane.example,,Christmas functions,"$3,400",Won,28/10/2026
  2. Tool call

    list_modules

    What it answered
    {
      "creationOrder": [
        "accounts",
        "contacts",
        "deals"
      ],
      "dateFormat": "DMY",
      "defaultEnabled": [],
      "expressionContext": "rec",
      "modules": [
        {
          "apiName": "Accounts",
          "defaultMatch": {
            "expression": "",
            "searchField": "",
            "searchMethod": "equals"
          },
          "key": "accounts",
          "label": "Accounts",
          "singular": "Account",
          "uniqueFields": [
            "Account_Number"
          ]
        },
        {
          "apiName": "Contacts",
          "defaultMatch": {
            "expression": "",
            "searchField": "",
            "searchMethod": "equals"
          },
          "key": "contacts",
          "label": "Contacts",
          "singular": "Contact",
          "uniqueFields": []
        },
        {
          "apiName": "Deals",
          "defaultMatch": {
            "expression": "",
            "searchField": "",
            "searchMethod": "equals"
          },
          "key": "deals",
          "label": "Deals",
          "singular": "Deal",
          "uniqueFields": []
        }
      ],
      "timezone": "Australia/Sydney"
    }
  3. Tool call

    get_fieldsmodule: accounts

    What it answered
    {
      "fields": [
        {
          "aliases": [],
          "apiName": "Account_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Account Name",
          "lookupTarget": null,
          "maxLength": 200,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": true,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Account_Number",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Account Number",
          "lookupTarget": null,
          "maxLength": 40,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Account_Type",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "picklist",
          "label": "Account Type",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 11,
          "options": [
            "Analyst",
            "Competitor",
            "Customer",
            "Distributor",
            "Integrator",
            "Investor",
            "Other",
            "Partner",
            "Press",
            "Prospect",
            "Reseller"
          ],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Annual_Revenue",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "currency",
          "label": "Annual Revenue",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Billing_City",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Billing City",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Billing_Code",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Billing Code",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Billing_Country",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Billing Country",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Billing_State",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "picklist",
          "label": "Billing State",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 602,
          "options": [
            "-None-",
            "Australian Capital Territory",
            "California",
            "New South Wales",
            "New York",
            "Northern Territory",
            "Queensland",
            "South Australia",
            "Tasmania",
            "Texas",
            "Victoria (Australia)",
            "Victoria (Malta)",
            "Washington",
            "Western Australia",
            " Daegu",
            " Jeollanam-do",
            "Abyan",
            "Agusan del Norte",
            "Agusan del Sur",
            "Ahal Region",
            "Al Bayda'",
            "Al Daayen",
            "Al Janūbịyah",
            "Al Madinah",
            "Al Muḩarraq",
            "Al Wakrah",
            "Al Wusţá",
            "Al ‘Āşimah",
            "Al-Hasakah",
            "Al-Qassim",
            "Alagoas",
            "Alajuela",
            "Alaska",
            "Alberta",
            "Alto Paraguay",
            "Amnat Charoen",
            "Andaman and Nicobar Islands",
            "Anibare District",
            "Anjouan",
            "Antalya",
            "Appenzell Innerrhoden",
            "Ardahan",
            "Arkansas",
            "Armavir Region",
            "Artemisa Province",
            "As-Suwayda",
            "Atakora",
            "Atlantique",
            "Atlántico",
            "Atua",
            "Atyrau Region",
            "Au Cap",
            "Ayacucho",
            "Azua",
            "Badajoz",
            "Baghlan",
            "Bagmati Zone",
            "Baja California Sur",
            "Balakan District",
            "Balzan"
          ],
          "optionsTruncated": true,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Billing_Street",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Billing Street",
          "lookupTarget": null,
          "maxLength": 250,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Description",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "textarea",
          "label": "Description",
          "lookupTarget": null,
          "maxLength": 32000,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Employees",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "integer",
          "label": "Employees",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Industry",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "picklist",
          "label": "Industry",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 9,
          "options": [
            "Retail",
            "Technology",
            "Manufacturing",
            "Logistics",
            "Communications",
            "Consulting",
            "Education",
            "Financial Services",
            "Government"
          ],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Phone",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "phone",
          "label": "Phone",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Tax_Number",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Tax Number",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Website",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "website",
          "label": "Website",
          "lookupTarget": null,
          "maxLength": 255,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        }
      ],
      "module": "accounts"
    }
  4. Tool call

    get_fieldsmodule: contacts

    What it answered
    {
      "fields": [
        {
          "aliases": [],
          "apiName": "Account_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "lookup",
          "label": "Account Name",
          "lookupTarget": "Accounts",
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Description",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "textarea",
          "label": "Description",
          "lookupTarget": null,
          "maxLength": 32000,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Email",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "email",
          "label": "Email",
          "lookupTarget": null,
          "maxLength": 100,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Fee_Paid",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "currency",
          "label": "Fee Paid",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "First_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "First Name",
          "lookupTarget": null,
          "maxLength": 40,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Last_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Last Name",
          "lookupTarget": null,
          "maxLength": 80,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": true,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Lead_Source",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "picklist",
          "label": "Lead Source",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 11,
          "options": [
            "Advertisement",
            "Chat",
            "Cold Call",
            "Employee Referral",
            "External Referral",
            "Online Store",
            "Partner",
            "Public Relations",
            "Trade Show",
            "Web Download",
            "Web Research"
          ],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Mailing_City",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Mailing City",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Mailing_Country",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Mailing Country",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Mobile",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "phone",
          "label": "Mobile",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Phone",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "phone",
          "label": "Phone",
          "lookupTarget": null,
          "maxLength": 30,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Registered_On",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "date",
          "label": "Registered On",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Session_Date",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "date",
          "label": "Session Date",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Title",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Title",
          "lookupTarget": null,
          "maxLength": 100,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        }
      ],
      "module": "contacts"
    }
  5. Tool call

    get_fieldsmodule: deals

    What it answered
    {
      "fields": [
        {
          "aliases": [],
          "apiName": "Account_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "lookup",
          "label": "Account Name",
          "lookupTarget": "Accounts",
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Amount",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "currency",
          "label": "Amount",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Campaign_Source",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Campaign Source",
          "lookupTarget": null,
          "maxLength": 100,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Closing_Date",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "date",
          "label": "Closing Date",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Contact_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "lookup",
          "label": "Contact Name",
          "lookupTarget": "Contacts",
          "maxLength": null,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": false,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Deal_Name",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "text",
          "label": "Deal Name",
          "lookupTarget": null,
          "maxLength": 120,
          "mustBe": null,
          "optionCount": 0,
          "options": [],
          "optionsTruncated": false,
          "required": true,
          "writtenAs": null
        },
        {
          "aliases": [],
          "apiName": "Stage",
          "derived": false,
          "description": null,
          "excludeFromAi": false,
          "format": null,
          "kind": "picklist",
          "label": "Stage",
          "lookupTarget": null,
          "maxLength": null,
          "mustBe": null,
          "optionCount": 7,
          "options": [
            "Qualification",
            "Needs Analysis",
            "Value Proposition",
            "Proposal",
            "Negotiation",
            "Closed Won",
            "Closed Lost"
          ],
          "optionsTruncated": false,
          "required": true,
          "writtenAs": null
        }
      ],
      "module": "deals"
    }
  6. Tool call

    propose_matchingcolumns: ["Company","Contact","Job title","E…, modules: ["accounts","contacts","deals"], sampleRows: [{"Compa…

    What it answered
    {
      "credits": 5,
      "declinedAgain": [],
      "modules": [
        "accounts",
        "contacts",
        "deals"
      ],
      "notes": [],
      "requestId": "01a100aa-f441-76cc-9e1a-757bbaa6923d",
      "rules": [
        {
          "confidence": 0.6,
          "expression": "helpers.trim(row.Company)",
          "field": "Account_Name",
          "method": "equals",
          "module": "accounts",
          "outputs": {
            "rows": 3,
            "samples": [
              "Brightwater Joinery Pty Ltd",
              "saltbush solar co",
              "Tidewell Plumbing"
            ]
          },
          "reason": "The file has no account number, so the company name is the best way to recognise an existing account."
        },
        {
          "confidence": 0.75,
          "expression": "helpers.lower(helpers.trim(row.Email))",
          "field": "Email",
          "method": "equals",
          "module": "contacts",
          "outputs": {
            "rows": 3,
            "samples": [
              "priya.r@brightwater.example",
              "aisha.rahman@saltbush.exampl…",
              "sam@tidewell.example"
            ]
          },
          "reason": "Each contact has a distinct email address, which is the most reliable way to recognise an existing contact."
        },
        {
          "confidence": 0.55,
          "expression": "helpers.trim(row.Opportunity)",
          "field": "Deal_Name",
          "method": "equals",
          "module": "deals",
          "outputs": {
            "rows": 3,
            "samples": [
              "Workshop fit-out",
              "Warehouse roof array",
              "Service agreement"
            ]
          },
          "reason": "The opportunity name is the only distinct text that can identify an existing deal."
        }
      ],
      "usage": {
        "cacheCreationInputTokens": 7814,
        "cacheReadInputTokens": 0,
        "inputTokens": 1439,
        "outputTokens": 274
      }
    }
  7. Tool call

    propose_mappingcolumns: ["Company","Contact","Job title","E…, enabledModules: ["accounts","contacts","deals"], sampleRows: […

    What it answered
    {
      "columns": [
        {
          "apiName": "Account_Name",
          "column": "Company",
          "confidence": 0.95,
          "mode": "column",
          "module": "accounts",
          "reason": "Company names fill the account name directly.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Account_Name",
          "column": "Company",
          "confidence": 0.9,
          "mode": "expression",
          "module": "contacts",
          "outputs": {
            "rows": 0,
            "runtime": true,
            "samples": []
          },
          "reason": "Accounts are imported first, so each contact links to the account created from the same row.",
          "searchField": null,
          "status": "accepted",
          "transform": "rec.Account.id"
        },
        {
          "apiName": "Account_Name",
          "column": "Company",
          "confidence": 0.9,
          "mode": "expression",
          "module": "deals",
          "outputs": {
            "rows": 0,
            "runtime": true,
            "samples": []
          },
          "reason": "Each deal links to the account created from the same row.",
          "searchField": null,
          "status": "accepted",
          "transform": "rec.Account.id"
        },
        {
          "apiName": "First_Name",
          "column": "Contact",
          "confidence": 0.8,
          "mode": "expression",
          "module": "contacts",
          "outputs": {
            "rows": 3,
            "runtime": false,
            "samples": [
              "Priya",
              "Aisha",
              "Sam"
            ]
          },
          "reason": "The first word of the full name becomes the first name.",
          "searchField": null,
          "status": "accepted",
          "transform": "helpers.split(row.Contact, ' ', 0)"
        },
        {
          "apiName": "Last_Name",
          "column": "Contact",
          "confidence": 0.75,
          "mode": "expression",
          "module": "contacts",
          "outputs": {
            "rows": 3,
            "runtime": false,
            "samples": [
              "Ramanathan",
              "Rahman",
              "Nguyen"
            ]
          },
          "reason": "The last word of the full name becomes the last name, which is required.",
          "searchField": null,
          "status": "accepted",
          "transform": "helpers.split(row.Contact, ' ', 1)"
        },
        {
          "apiName": "Contact_Name",
          "column": "Contact",
          "confidence": 0.9,
          "mode": "expression",
          "module": "deals",
          "outputs": {
            "rows": 0,
            "runtime": true,
            "samples": []
          },
          "reason": "Each deal links to the contact created from the same row.",
          "searchField": null,
          "status": "accepted",
          "transform": "rec.Contact.id"
        },
        {
          "apiName": "Title",
          "column": "Job title",
          "confidence": 0.95,
          "mode": "column",
          "module": "contacts",
          "reason": "Job titles fit the contact title field.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Email",
          "column": "Email",
          "confidence": 0.97,
          "mode": "column",
          "module": "contacts",
          "reason": "These are email addresses and fit the email field.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Mobile",
          "column": "Mobile",
          "confidence": 0.95,
          "mode": "column",
          "module": "contacts",
          "reason": "Mobile numbers go straight into the mobile field.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Deal_Name",
          "column": "Opportunity",
          "confidence": 0.9,
          "mode": "column",
          "module": "deals",
          "reason": "Opportunity names are the deal names.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Amount",
          "column": "Value",
          "confidence": 0.9,
          "mode": "expression",
          "module": "deals",
          "outputs": {
            "rows": 3,
            "runtime": false,
            "samples": [
              "48500",
              "96750",
              "9600"
            ]
          },
          "reason": "Strips the dollar sign and commas to give a number.",
          "searchField": null,
          "status": "accepted",
          "transform": "helpers.num(row.Value)"
        },
        {
          "apiName": "Stage",
          "column": "Stage",
          "confidence": 0.85,
          "mode": "column",
          "module": "deals",
          "reason": "Stage values are matched to the listed options at Translate values.",
          "searchField": null,
          "status": "accepted",
          "transform": null
        },
        {
          "apiName": "Closing_Date",
          "column": "Expected close",
          "confidence": 0.85,
          "mode": "expression",
          "module": "deals",
          "outputs": {
            "rows": 3,
            "runtime": false,
            "samples": [
              "2026-11-15",
              "2026-12-20",
              "2026-12-05"
            ]
          },
          "reason": "Dates are mostly day/month/year, and ISO dates like 2026-12-05 are also read.",
          "searchField": null,
          "status": "accepted",
          "transform": "helpers.date(row[\"Expected close\"], \"DD/MM/YYYY\")"
        }
      ],
      "credits": 6,
      "enabledModules": [
        "accounts",
        "contacts",
        "deals"
      ],
      "mappings": {
        "accounts": {
          "Account_Name": {
            "mode": "column",
            "skip": false,
            "source": "Company",
            "staticValue": null,
            "transform": null
          }
        },
        "contacts": {
          "Account_Name": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "rec.Account.id"
          },
          "Email": {
            "mode": "column",
            "skip": false,
            "source": "Email",
            "staticValue": null,
            "transform": null
          },
          "First_Name": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "helpers.split(row.Contact, ' ', 0)"
          },
          "Last_Name": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "helpers.split(row.Contact, ' ', 1)"
          },
          "Mobile": {
            "mode": "column",
            "skip": false,
            "source": "Mobile",
            "staticValue": null,
            "transform": null
          },
          "Title": {
            "mode": "column",
            "skip": false,
            "source": "Job title",
            "staticValue": null,
            "transform": null
          }
        },
        "deals": {
          "Account_Name": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "rec.Account.id"
          },
          "Amount": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "helpers.num(row.Value)"
          },
          "Closing_Date": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "helpers.date(row[\"Expected close\"], \"DD/MM/YYYY\")"
          },
          "Contact_Name": {
            "mode": "expression",
            "skip": false,
            "source": null,
            "staticValue": null,
            "transform": "rec.Contact.id"
          },
          "Deal_Name": {
            "mode": "column",
            "skip": false,
            "source": "Opportunity",
            "staticValue": null,
            "transform": null
          },
          "Stage": {
            "mode": "column",
            "skip": false,
            "source": "Stage",
            "staticValue": null,
            "transform": null
          }
        }
      },
      "notes": [],
      "requestId": "01a100ab-012d-7713-a341-0745256456e1",
      "usage": {
        "cacheCreationInputTokens": 7843,
        "cacheReadInputTokens": 0,
        "inputTokens": 620,
        "outputTokens": 1092
      }
    }
  8. Tool call

    test_expressioncolumns: ["Expected close"], expression: helpers.date(row["Expected close"],…, sampleRows: [{"Expected close"…

    What it answered
    {
      "compileError": null,
      "compiled": true,
      "firstError": null,
      "ok": true,
      "referencesRecords": false,
      "results": [
        {
          "error": null,
          "row": 1,
          "value": "2026-11-15"
        },
        {
          "error": null,
          "row": 2,
          "value": "2026-12-01"
        },
        {
          "error": null,
          "row": 3,
          "value": "2026-12-05"
        },
        {
          "error": null,
          "row": 4,
          "value": "2026-10-28"
        }
      ]
    }
  9. Tool call

    test_expressioncolumns: ["Company"], expression: helpers.trim(row.Company).replace(/…, sampleRows: [{"Company":"Saltbush Sol…

    What it answered
    {
      "compileError": null,
      "compiled": true,
      "firstError": null,
      "ok": true,
      "referencesRecords": false,
      "results": [
        {
          "error": null,
          "row": 1,
          "value": "Saltbush Solar Co"
        },
        {
          "error": null,
          "row": 2,
          "value": "saltbush solar co"
        },
        {
          "error": null,
          "row": 3,
          "value": "Tidewell Plumbing"
        }
      ]
    }
  10. Tool call

    validate_templateenabledModules: ["accounts","contacts","deals"], columns: ["Company","Contact","Job title","E…, fieldMappings…

    What it answered
    {
      "issueCount": 0,
      "issues": {
        "accounts": {
          "expressions": [],
          "picklistValues": [],
          "types": []
        },
        "contacts": {
          "expressions": [],
          "picklistValues": [],
          "types": []
        },
        "deals": {
          "expressions": [],
          "picklistValues": [],
          "types": []
        }
      },
      "libraryProblems": [],
      "ok": true,
      "problems": []
    }
  11. Tool call

    save_templatename: Expo leads, description: Expo lead sheet: one account, conta…, config: {"enabledModules":["accounts","c…

    What it answered
    {
      "created": true,
      "id": "01a100ab-83f4-7070-bee1-abef43545f90",
      "updatedAt": "2026-10-03T07:30:02.868Z",
      "version": 1
    }
  12. Assistant

    I've saved the "Expo leads" template (id 01a100ab-83f4-7070-bee1-abef43545f90). It passed validation with no issues. I haven't run an import with it, so it hasn't been tried on your actual file.

    What it does for each row

    • Account: the name comes from Company, with spaces and any trailing full stop removed. Existing accounts are matched on that name.
    • Contact: the first and last name are split from Contact. Title, Email and Mobile come across as they are. The contact links to the row's account and is matched on lowercase email.
    • Deal: Deal Name comes from Opportunity and Amount from Value, with $ and commas removed. Closing Date is read as day/month/year, and it handled 15/11/2026, 1/12/2026 and the ISO 2026-12-05. The deal links to the row's account and contact and is matched on deal name.

    Stage translation. Your stages aren't Zoho's, so I mapped them as follows. Please check these:

    • Quote sent (any capitalisation) becomes Proposal.
    • Qualified becomes Qualification.
    • Discovery becomes Needs Analysis.
    • Negotiation stays Negotiation.
    • Won becomes Closed Won.

    Things to check

    • Saltbush: "Saltbush Solar Co." and "saltbush solar co" should become one account, but only if Zoho's name match ignores capitalisation. I couldn't confirm that. If it doesn't, you'll get a duplicate Saltbush account, which is easy to merge afterwards.
    • Warehouse roof array: it appears twice, once for Marcus and once for Aisha. Matching on deal name means it should be created once, linked to Marcus (the first row). Aisha will still be created as a contact on the same account.
    • Deal matching: deals are matched on name alone, so two different companies with an identically named deal would be treated as the same deal.
    • Contact names: the split assumes two-word names, which is true for every row here. A name like "Mary Ann Smith" would break.
    • Empty mobile: Grace Liu's mobile is blank, so that field is left empty.
    • Lead Source: I didn't set it. If you want these contacts tagged as "Trade Show", that's a one-line addition.
An assistant proposes and saves a template. Nothing is imported until a person runs it.

What keeps it safe

  • Sloose's AI never writes a record, and no MCP tool runs an import.
  • Field choices are checked against your CRM's real fields, and each rule is run on sample rows before it is shown.
  • A rule the AI wrote runs in your browser only once Sloose has checked it reads nothing but the row, or once you choose to use it.
  • MCP is on the Business and Enterprise plans and during a trial. You can take an assistant's access away under Your account, Apps with access.

More in the docs: connect an MCP client and how Sloose uses AI.

Questions people ask

Can ChatGPT clean Excel data?

Yes. ChatGPT, Claude and Copilot can all tidy a spreadsheet, often by writing and running code, and hand you a new file. What you get is a new file to check by hand before you import it.

Sloose cleans the file as part of the import. The AI writes one rule per column, you see what each rule produces on your rows, and Validate and a dry run check the result before anything is written. See AI mapping.

How do I connect Claude to Zoho CRM?

Zoho publishes its own MCP server, which lets Claude and other MCP clients create, read, update and delete records. Its calls use your Zoho API credits like any other API call. (Zoho)

Sloose's MCP server does a different job: Claude reads your CRM's fields through Sloose, proposes an import, tests it and saves it as a template, and a person runs it in Sloose. It is on the Business and Enterprise plans and during a trial. See API and MCP and the MCP docs.

Is it safe to give an AI assistant write access to my CRM?

It depends on what it can write and what you check first. An assistant with your CRM's own MCP server writes records directly, one tool call at a time.

Sloose's MCP tools cannot write records or run an import. An assistant can only propose and save a template, which a person reviews, dry-runs and runs. It acts with the role you hold in the organisation, and you can take its access away under Your account, Apps with access.

Does Sloose use AI credits when it runs an import?

No. Running an import, a dry run and a retry never use AI credits. Rows count against your plan's runs and rows. Credits are used only when someone presses an AI button, or an assistant calls propose_mapping or propose_matching. See pricing.

Can Zia or Breeze import a spreadsheet for me?

They are the assistants built into Zoho CRM and HubSpot, and they work inside their own CRM. We have not compared their import help here. One limit worth knowing for bulk work: HubSpot's Breeze Assistant can generate content up to 1,000 times a day. (HubSpot)

Sloose connects to Zoho CRM today. More CRMs on the way.

Bring your assistant. Keep the review.

Connect Claude or another MCP client to Sloose, or start on the free plan with your own spreadsheet.

Works with your AIStart free