Validate Phone Numbers in Zoho CRM with the Neutrino API and Deluge

Parse, Validate and Locate a Phone Number with the Neutrino API

Bad phone numbers quietly destroy outbound campaigns. This script runs every number through the Neutrino API before it is trusted: it confirms the number is real, tells you whether it is a mobile or a landline, normalises it to E.164, and writes it to the right field. When a number fails, it raises a task in Zoho CRM instead of silently discarding it.

Updated in three places. The host moved — Neutrino documents the API at neutrinoapi.net now, not neutrinoapi.com. Authentication moved to the User-ID and API-Key headers; the old form-parameter style still works but is no longer the documented default. And the original used zoho.crm._create and zoho.crm._updateRecord, which are deprecated task names — the current ones are zoho.crm.createRecord and zoho.crm.updateRecord. The valid and type response fields are unchanged, so the logic still holds.

Before you start

  • A Neutrino API account. The free tier still exists and is enough to test with; paid plans start around $10/month.
  • Your Neutrino User ID and API key, stored as Zoho variables rather than inline.
  • Running from Creator against CRM? You will need a Zoho OAuth connection with ZohoCRM.modules.ALL and to pass connection:"your_connection" to the zoho.crm tasks.

Deluge script

// Trigger: on a Zoho CRM Lead, or from a Creator function
apiKey = "YOUR_NEUTRINO_API_KEY";
userId = "YOUR_NEUTRINO_USER_ID";

phoneNumber = if(!isBlank(Mobile), Mobile, Phone);
if(isBlank(phoneNumber))
{
    return;
}

headerMap = Map();
headerMap.put("User-ID",userId);
headerMap.put("API-Key",apiKey);

bodyMap = Map();
bodyMap.put("number",phoneNumber);
// bodyMap.put("country-code","US");   // optional, helps with local-format numbers

resp = invokeurl
[
    url    : "https://neutrinoapi.net/phone-validate"
    type   : POST
    parameters : bodyMap
    headers: headerMap
];

// Neutrino signals auth and quota problems in the body, not the status code
if(resp.getJSON("api-error") != null)
{
    info "Neutrino error " + resp.getJSON("api-error") + ": " + resp.getJSON("api-error-msg");
    return;
}

isValid = resp.getJSON("valid");

if(isValid == false)
{
    taskInfo = Map();
    taskInfo.put("Subject","Phone not valid");
    taskInfo.put("Description","Neutrino rejected " + phoneNumber + ". Please verify with the contact.");
    taskInfo.put("What_Id",LeadID);
    taskInfo.put("$se_module","Leads");
    createResp = zoho.crm.createRecord("Tasks",taskInfo);
    info createResp;
}
else
{
    lineType = resp.getJSON("type");          // mobile | fixed-line | premium-rate | toll-free | voip | unknown
    e164     = resp.getJSON("international-number");

    updateMap = Map();
    if(lineType == "mobile")
    {
        updateMap.put("Mobile",e164);
    }
    else
    {
        updateMap.put("Phone",e164);
    }
    updateResp = zoho.crm.updateRecord("Leads",LeadID.toString(),updateMap);
    info updateResp;
}

Notes

  • Errors arrive with HTTP 200. Neutrino reports auth failures and quota exhaustion in an api-error field in the body. Without the check above, an expired key reads as “valid is null” and every number silently looks invalid.
  • Store international-number, not the raw input. Normalising to E.164 on the way in is what makes deduplication work later.
  • Response keys are kebab-case by default — country-code, is-mobile, international-number. Pass output-case if you prefer camel or snake.
  • voip deserves its own handling. A VoIP number is valid but often will not accept SMS. If you text your leads, branch on it rather than treating it as a mobile.
  • Watch your quota on bulk runs. Validating an existing database of 50,000 leads will burn through a plan quickly. Validate on create and edit, then backfill in scheduled batches.

This script is part of the free Creator Scripts Deluge Library.

All 39 Deluge scripts, the full Zoho Creator course, and every downloadable asset are now free. Get free access →