curl --request POST \
--url https://public-api.lonescale.com/crm/search \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"email": "<string>",
"linkedin_url": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"company_domain": "<string>",
"company_name": "<string>"
}
'import requests
url = "https://public-api.lonescale.com/crm/search"
payload = {
"email": "<string>",
"linkedin_url": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"company_domain": "<string>",
"company_name": "<string>"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
linkedin_url: '<string>',
first_name: '<string>',
last_name: '<string>',
company_domain: '<string>',
company_name: '<string>'
})
};
fetch('https://public-api.lonescale.com/crm/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://public-api.lonescale.com/crm/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'linkedin_url' => '<string>',
'first_name' => '<string>',
'last_name' => '<string>',
'company_domain' => '<string>',
'company_name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://public-api.lonescale.com/crm/search"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://public-api.lonescale.com/crm/search")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://public-api.lonescale.com/crm/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"matches": [
{
"crm": "hubspot",
"id": "<string>",
"name": "<string>",
"email": "<string>",
"title": "<string>",
"profileUrl": "<string>",
"url": "<string>",
"company": "<string>",
"companyDomain": "<string>",
"ownerId": "<string>",
"ownerName": "<string>",
"matchedEmail": "<string>",
"matchedOn": "primary_email",
"confidence": "high",
"owned_by_you": true
}
],
"caller_crm_links": {
"hubspot": {
"ownerId": "<string>"
},
"salesforce": {
"ownerId": "<string>"
}
},
"searched_crms": [
"<string>"
],
"default_crm": "<string>",
"errors": [
{
"crm": "<string>",
"error": "<string>"
}
]
}{
"message": "Invalid API Key",
"error": "Unauthorized",
"statusCode": 401
}{
"message": "email or linkedin_url is required",
"error": "Unprocessable Entity",
"statusCode": 422
}CRM Search
Look up a contact in your connected CRM(s) and surface the record owner. Provide email, linkedin_url, or a name with a company. Only connected CRMs are queried. Pipedrive and Sellsy search is email-only, so a linkedin-only lookup returns HubSpot/Salesforce matches only. Each match carries owned_by_you, and caller_crm_links tells which CRM user the caller is linked to, so a client can say “already yours” vs “owned by X” and warn when a create would go to the default owner.
curl --request POST \
--url https://public-api.lonescale.com/crm/search \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"email": "<string>",
"linkedin_url": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"company_domain": "<string>",
"company_name": "<string>"
}
'import requests
url = "https://public-api.lonescale.com/crm/search"
payload = {
"email": "<string>",
"linkedin_url": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"company_domain": "<string>",
"company_name": "<string>"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: '<string>',
linkedin_url: '<string>',
first_name: '<string>',
last_name: '<string>',
company_domain: '<string>',
company_name: '<string>'
})
};
fetch('https://public-api.lonescale.com/crm/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://public-api.lonescale.com/crm/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'linkedin_url' => '<string>',
'first_name' => '<string>',
'last_name' => '<string>',
'company_domain' => '<string>',
'company_name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://public-api.lonescale.com/crm/search"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://public-api.lonescale.com/crm/search")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://public-api.lonescale.com/crm/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"linkedin_url\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"company_domain\": \"<string>\",\n \"company_name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"matches": [
{
"crm": "hubspot",
"id": "<string>",
"name": "<string>",
"email": "<string>",
"title": "<string>",
"profileUrl": "<string>",
"url": "<string>",
"company": "<string>",
"companyDomain": "<string>",
"ownerId": "<string>",
"ownerName": "<string>",
"matchedEmail": "<string>",
"matchedOn": "primary_email",
"confidence": "high",
"owned_by_you": true
}
],
"caller_crm_links": {
"hubspot": {
"ownerId": "<string>"
},
"salesforce": {
"ownerId": "<string>"
}
},
"searched_crms": [
"<string>"
],
"default_crm": "<string>",
"errors": [
{
"crm": "<string>",
"error": "<string>"
}
]
}{
"message": "Invalid API Key",
"error": "Unauthorized",
"statusCode": 401
}{
"message": "email or linkedin_url is required",
"error": "Unprocessable Entity",
"statusCode": 422
}Authorizations
Provide your API key in the x-api-key header.
Body
Provide email, linkedin_url, or a name (first_name/last_name) together with company_domain or company_name. A name is never searched unconstrained.
Email to look up in the connected CRM(s).
LinkedIn profile URL to look up. HubSpot/Salesforce only.
Used with last_name + a company when no email/LinkedIn is available. Matches are fuzzier and reported with a lower confidence.
Constrains a name search to that company. Preferred over company_name (more precise).
Fallback company constraint when no domain is known. Yields confidence: low.
Restrict the search to a single CRM instead of all connected ones.
hubspot, salesforce, pipedrive, sellsy Response
CRM matches with owner
Show child attributes
Show child attributes
The CRM user linked to the API key holder (Settings > Members), per CRM. Records created by a CRM write-back (sync_to_crm, Chrome extension) are assigned to that user; null means not linked, so created records go to the connection's default owner. Pipedrive/Sellsy have no per-user link.
Show child attributes
Show child attributes
The connected CRMs actually queried.
The CRM a crm-less sync would resolve to (same rules as POST /trigger/crm-sync). Null when ambiguous: ask the user which CRM to act on.
Per-CRM lookup failures. Present only when a provider errored. IMPORTANT: an empty matches alongside errors is NOT a confirmed "no match" — do not treat it as "contact is new".
Show child attributes
Show child attributes

