# Available Fields Source: https://docs.lonescale.com/api-reference/crm-sync-fields The records you send to [CRM Sync](/api-reference/endpoint/crm-sync) are free-form JSON objects. Their keys are **LoneScale field keys**, the same vocabulary your CRM integration mapping is built on. Two rules decide what actually reaches your CRM: 1. LoneScale only reads the keys your mapping references. A key you send that no mapped field and no unicity criterion points at is ignored. 2. A record whose keys match **none** of them is failed rather than silently written as a no-op, and the error lists the keys that mapping expects. Call [`GET /crm/mapping`](/api-reference/endpoint/crm-mapping) to see exactly which of the fields below your own mapping uses, before pushing anything. **The prefix is optional.** Every key below can be sent without its `lonescale_` or `main_` prefix: `first_name`, `job_title`, `company_domain`, `email`, `phone`. Both forms work, and the bare one never overrides a prefixed key you sent explicitly. ## People | Field key | Description | | ----------------------------- | -------------------------------------------------------- | | `lonescale_first_name` | First name | | `lonescale_last_name` | Last name | | `lonescale_full_name` | Full name | | `lonescale_linkedin_url` | LinkedIn profile URL | | `lonescale_universal_name` | LinkedIn public identifier (the slug in the profile URL) | | `lonescale_job_title` | Job title | | `lonescale_job_seniority` | Seniority level | | `lonescale_department` | Department | | `lonescale_sub_department` | Sub-department | | `lonescale_level` | Hierarchical level | | `lonescale_summary` | Profile summary | | `lonescale_city` | City | | `lonescale_geo_location_name` | Location name | | `lonescale_geo_country_name` | Country name | | `lonescale_country_code` | Country code | ## Contact details | Field key | Description | | ------------------------- | ---------------------------------------------- | | `main_email` | Primary email | | `main_email_status` | Deliverability status of the primary email | | `secondary_emails` | Additional emails | | `secondary_emails_status` | Deliverability status of the additional emails | | `main_phone` | Primary phone | | `main_phone_region` | Region code of the primary phone | | `secondary_phones` | Additional phones | | `secondary_phones_region` | Region codes of the additional phones | ## Company Company keys can be embedded on a contact record (to create or update the associated account at the same time) or sent on their own in the `companies` array. | Field key | Description | | ---------------------------------- | -------------------- | | `lonescale_company_name` | Company name | | `lonescale_company_domain` | Company domain | | `lonescale_company_description` | Company description | | `lonescale_company_industry` | Industry | | `lonescale_company_linkedin_url` | Company LinkedIn URL | | `lonescale_company_linkedin_id` | Company LinkedIn id | | `lonescale_company_staff_count` | Employee count | | `lonescale_company_employee_range` | Employee range | | `lonescale_company_country` | Country | | `lonescale_company_country_code` | Country code | ## CRM ids | Field key | Description | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `contact_crm_id` | Update this exact CRM contact by id (for example the id returned by [`POST /crm/search`](/api-reference/endpoint/crm-search)), skipping unicity matching | | `account_crm_id` | Target this exact CRM company by id instead of matching by unicity criteria | | `crm_company_id` | The associated company id, available to your mapping as a value to write | ## Accepted aliases Field keys are resolved before the mapping runs, so several spellings of the same field work. **Without the prefix.** Drop `lonescale_` or `main_` from any key in the tables above and it still resolves: `first_name`, `full_name`, `job_title`, `linkedin_url`, `department`, `city`, `country_code`, `company_name`, `company_domain`, `company_staff_count`, `email`, `email_status`, `phone`, `phone_region`. Person and company keys stay distinct: `city` is the contact's, `company_city` is the account's. **From an enrich payload.** You can also send our enrich output verbatim. | You send | Resolved to | | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | `firstName`, `lastName`, `fullName` | `lonescale_first_name`, `lonescale_last_name`, `lonescale_full_name` | | `linkedinUrl`, `universalName` | `lonescale_linkedin_url`, `lonescale_universal_name` | | `jobTitle` | `lonescale_job_title` | | `email`, `emailStatus` | `main_email`, `main_email_status` | | `phone`, `phoneCountryCode` | `main_phone`, `main_phone_region` | | `lonescale_email`, `lonescale_phone` | `main_email`, `main_phone` | | `companyName`, `companyDomain`, `companyDescription`, `companyIndustry` | the matching `lonescale_company_*` key | | `companyEmployeeCount`, `companyEmployeeRange` | `lonescale_company_staff_count` and `lonescale_company_employee_count`, `lonescale_company_employee_range` | | `companyLinkedinUrl`, `companyLinkedinId`, `companyCity`, `companyCountry`, `companyCountryCode` | the matching `lonescale_company_*` key | | any bare `company_*` key | the matching `lonescale_company_*` key | An alias never overwrites a key you sent explicitly. ## Salesforce: Contacts or Leads For Salesforce, the mapping also decides **which object** is written, exactly like in a workflow. No parameter on the request controls it: * Map the Contact section and the sync writes Contacts. * Map only the Lead section, leaving the Contact one empty, and the sync searches your Lead unicity criteria, then updates the match or creates a Lead. `GET /crm/mapping` reports this as `object: "contact"` or `object: "lead"`, and returns the resolved lead mapping alongside it. Salesforce requires `Company` on Lead creation, so map it from `lonescale_company_name` and send that key on every record that may create a Lead. ## Example ```json theme={null} { "crm": "salesforce", "settings_type": "api_sync", "contacts": [ { "first_name": "Ada", "last_name": "Lovelace", "job_title": "Head of Data", "linkedin_url": "https://www.linkedin.com/in/ada-lovelace", "email": "ada@acme.com", "phone": "+33600000000", "company_name": "Acme", "company_domain": "acme.com" } ] } ``` The same record written as `lonescale_first_name`, `main_email` and so on is equivalent. # Search Company Source: https://docs.lonescale.com/api-reference/endpoint/companies-search GET /companies/search Resolve a single company by domain, LinkedIn id, LinkedIn slug, or name and return a stable, fixed-shape result. At least one of `domain`, `linkedin_id`, `slug`, or `name` is required. The response always returns the same set of fields — `null` (or `[]` for arrays) is used for any field we do not have data for, so client integrations can rely on a stable schema. When `enrich=true` and no cached match is found, the API will trigger an on-demand enrichment via data-sync; when `enrich=true` and a result has both a LinkedIn id and a universal name, a function-by-function headcount breakdown is also attached. The optional `custom` map (e.g. `custom[crm_id]=abc-123`) is echoed back unchanged at the top level of the response — useful for correlating results with records on your side. Rate limited to **5 requests per minute**. # Search Contacts Source: https://docs.lonescale.com/api-reference/endpoint/contact-sourcing POST /trigger/contact-sourcing Search Contacts. LoneScale will call your `webhook_url` with this payload once the job is completed. See the [Sourcing payload](/api-reference/webhookpayload/sourcing-payload) documentation for the full webhook schema. # Search Contacts (Sync) Source: https://docs.lonescale.com/api-reference/endpoint/contact-sourcing-sync POST /trigger/contact-sourcing/sync Search contacts synchronously and return results directly in the response — no webhook required. The response contains all sourced contacts in a single call. **Maximum 10 contacts per request.** Rate limited to **5 requests per minute**. # Enrich Contacts Source: https://docs.lonescale.com/api-reference/endpoint/create POST /trigger/enrich Start waterfall enrichment for contacts. LoneScale will call your `webhook_url` with this payload once the job is completed. See the [Enrichment payload](/api-reference/webhookpayload/enrichment-payload) documentation for the full webhook schema. # CRM Mapping Preview Source: https://docs.lonescale.com/api-reference/endpoint/crm-mapping GET /crm/mapping Preview which CRM fields a `POST /trigger/crm-sync` will write: the team's resolved contact/company create & update field mappings, the unicity criteria used for matching, and whether creation is enabled. Read-only. # CRM Search Source: https://docs.lonescale.com/api-reference/endpoint/crm-search POST /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. # CRM Sync Source: https://docs.lonescale.com/api-reference/endpoint/crm-sync POST /trigger/crm-sync Push contacts and/or companies directly into your connected CRM. LoneScale creates or updates the records asynchronously, applying the team-level integration field mappings already configured in LoneScale (create/update fields and unicity criteria). No workflow is involved. Record keys must match the LoneScale field keys referenced by your configured mappings (for example `lonescale_first_name`, `lonescale_company_domain`, or any custom key you mapped); see [Available Fields](/api-reference/crm-sync-fields) for the full list. For Salesforce the mapping also decides the object: map only the Lead section and the sync searches, updates or creates Leads instead of Contacts. Set `account_crm_id` on a record to target an existing CRM company by id instead of matching by unicity criteria. Poll `GET /trigger/crm-sync/{id}` with the returned `id`, or pass a `webhook_url` to be called once with the run summary when every record has been processed. # CRM Sync Delivery Log Source: https://docs.lonescale.com/api-reference/endpoint/crm-sync-records GET /trigger/crm-sync/{id}/records Delivery log of a CRM sync run: one entry per record with its final status (`synced` / `failed` / `pending`), the CRM ids created or updated, the error message when it failed and the number of attempts. Records not consumed yet (run still processing) are reported as `pending`. # CRM Sync Status Source: https://docs.lonescale.com/api-reference/endpoint/crm-sync-status GET /trigger/crm-sync/{id} Get the status and progress counters of a CRM sync run. # Enrich Contacts (Sync) Source: https://docs.lonescale.com/api-reference/endpoint/enrich-sync POST /trigger/enrich/sync Enrich contacts synchronously and return results directly in the response — no webhook required. The response contains all enriched contacts in a single call. **Maximum 10 contacts per request.** Rate limited to **5 requests per minute, counted per API key** — contact us if your integration needs a higher limit, it can be raised per account. # Contact Enrichment History Source: https://docs.lonescale.com/api-reference/endpoint/enrichment-history GET /contacts/{identifier}/enrichment-history The last enrichment LoneScale holds for a contact, so you can keep your own long-lived cache and skip an enrichment you have already paid for. **This is a per-contact record, not a log of your calls.** Three limits follow from that, worth reading before you build on it: - At most **one entry per contact**: the latest state we know, not one entry per `/trigger/enrich` call. `providers` and `credits` are therefore not returned, since they are not retained per contact. - An enrichment that **found nothing leaves no entry**. A contact we searched and missed is indistinguishable from one we never searched: both return an empty `data`. - A hit on the 24h enrichment cache does **not** refresh `date`. It is the date of the last enrichment that actually ran, which is the date your cache should key on. The history is scoped to your API key: you only ever read contacts your own account enriched. # Get user information Source: https://docs.lonescale.com/api-reference/endpoint/get GET /users Get profile information # Add an item to a list Source: https://docs.lonescale.com/api-reference/endpoint/lists-add-item POST /lists/{listId}/item Add an item to a list. **Validation rules by entity type:** - **COMPANY**: must include `domain` OR both `name` and `location`. - **PEOPLE**: must include at least 2 of: `linkedin_url`, `name`, `location`, `first_name`, `last_name`, `full_name`, `company_name`, `current_position`. # Create a list Source: https://docs.lonescale.com/api-reference/endpoint/lists-create POST /lists Create a new imported list. # Delete a list Source: https://docs.lonescale.com/api-reference/endpoint/lists-delete DELETE /lists/{listId} Delete a list. The list must not be actively used by any workflows. # Remove an item from a list Source: https://docs.lonescale.com/api-reference/endpoint/lists-delete-item DELETE /lists/{listId}/item/{itemId} Remove an item from a list. # Get a list Source: https://docs.lonescale.com/api-reference/endpoint/lists-get GET /lists/{listId} Retrieve a specific list by ID. # Get all lists Source: https://docs.lonescale.com/api-reference/endpoint/lists-get-all GET /lists Retrieve a paginated list of imported lists for the authenticated user, optionally filtered by entity type. # Get list items Source: https://docs.lonescale.com/api-reference/endpoint/lists-get-items GET /lists/{listId}/items Retrieve paginated items from a specific list. # Verify Phone Number Source: https://docs.lonescale.com/api-reference/endpoint/verify-phone POST /verify-phone Verify Phone Numbers [Special Access Required] # List Webhooks Source: https://docs.lonescale.com/api-reference/endpoint/webhooks-list GET /webhooks List the platform webhooks configured for your team, so you can pass a `webhook_id` destination to `POST /trigger/crm-sync` (push records without a connected CRM). Deduped by URL. # Contact Enrichment Payload Source: https://docs.lonescale.com/api-reference/webhookpayload/enrichment-payload # Search Contacts Payload Source: https://docs.lonescale.com/api-reference/webhookpayload/sourcing-payload # Authentication Source: https://docs.lonescale.com/authentication You need to include a valid API key in the Authorization header of each request you make to our API. Each request to our API endpoints must include this authorization header. ```bash theme={null} x-api-key: YOUR_API_KEY ``` Your API key can be found on the Lonescale App > Integrations > Public API Be sure to replace YOUR\_API\_KEY with the actual API key. Without a valid API key in your request, you will not be able to access the API endpoints and will receive a 401 Unauthorized response. Keep your API key secure and do not share it publicly. # lonescale companies search Source: https://docs.lonescale.com/cli/companies-search Look up a single company on LoneScale by name, domain, LinkedIn slug, or LinkedIn ID. Returns one fixed-shape company profile (every field is always present, with `null` / `[]` for missing data). This is a synchronous call — results come back inline. ## Synopsis ```bash theme={null} lonescale companies search [--name ] [--domain ] [--linkedin-id ] [--slug ] [--enrich] [--custom ] ``` At least one of `--name`, `--domain`, `--linkedin-id`, `--slug` is required. ## Examples ```bash theme={null} # By domain lonescale companies search --domain stripe.com # By LinkedIn slug, with function-by-function headcount lonescale companies search --slug stripe --enrich # By name (partial match) lonescale companies search --name "Anthropic" # Attach custom passthrough metadata lonescale companies search --domain stripe.com \ --custom '{"campaign":"q2-pipeline","owner":"yann"}' ``` ## Options | Option | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `--name ` | Company name (partial match). | | `--domain ` | Company website domain. | | `--linkedin-id ` | LinkedIn company numeric ID. | | `--slug ` | LinkedIn universal name — the slug from `linkedin.com/company/`. | | `--enrich` | Fall back to live LoneScale enrichment if the company isn't cached, and fetch the function-by-function headcount. | | `--custom ` | Flat key/value JSON object. Echoed unchanged at the top of the response — useful for joining results back to a row id in your CRM / spreadsheet. | Plus all [global options](/cli/overview#global-options). ## Response shape ```json theme={null} { "linkedinId": "stripe", "linkedinUrl": "https://www.linkedin.com/company/stripe", "name": "Stripe", "domain": "stripe.com", "industry": "Financial Services", "staffCount": 8000, "headcount": { "engineering": 2400, "sales": 600, "marketing": 250 } } ``` Without `--enrich`, `headcount` is `null` for companies that haven't been previously enriched. `GET /companies/search` # lonescale enrich Source: https://docs.lonescale.com/cli/enrich Trigger waterfall enrichment (email and/or phone) for a list of contacts. By default the command is asynchronous — it returns a job ID immediately and you poll for results via [`lonescale result`](/cli/result), or pass `--wait` to have the CLI block until the job finishes. ## Synopsis ```bash theme={null} lonescale enrich --type [--contacts | --contacts-inline ] [options] ``` ## Examples ```bash theme={null} # From a JSON file, polling until done lonescale enrich --type email,phone --contacts contacts.json --wait # Inline JSON for a single contact lonescale enrich --type email --contacts-inline '[ { "firstname": "John", "lastname": "Doe", "domain": "acme.com" } ]' # Sync mode (returns results directly, rate-limited to 5 req/min) lonescale enrich --type email --contacts contacts.json --sync # Pipe from stdin (any source that produces a JSON array of contacts) cat contacts.json | lonescale enrich --type email ``` ## Options | Option | Description | | -------------------------- | ------------------------------------------------------------------------------------------------ | | `--type ` | **Required.** Comma-separated enrichment types: `email`, `phone`, or `email,phone`. | | `--contacts ` | Path to a JSON file containing the contacts array. | | `--contacts-inline ` | Inline JSON array (use when piping or scripting). | | `--custom ` | Custom metadata JSON, echoed back in the result and any webhook payload. | | `--wait` | Poll until the job completes. Default timeout is 120 s. | | `--sync` | Use the synchronous endpoint. Returns results inline but is rate-limited to 5 requests / minute. | | `--timeout ` | Override the `--wait` / `--sync` timeout (default 120). | Plus all [global options](/cli/overview#global-options) (`--api-key`, `--output`, `--debug`). ## Contact schema ```json theme={null} [ { "firstname": "John", "lastname": "Doe", "email": "john@acme.com", "job_title": "CTO", "linkedin_url": "https://linkedin.com/in/johndoe", "domain": "acme.com", "company_name": "Acme Inc" } ] ``` Each contact needs at least one identifier: `linkedin_url`, or `firstname` + `lastname` + (`domain` or `company_name`), or `email` alone. Every other field is optional — but more context means better match accuracy. **Email as a standalone identifier.** An `email` sent on its own resolves the person behind it — name, LinkedIn URL, job title, company — as long as `enrichment_type` includes `profile`. Add `email` / `phone` to the same request to also get their contact details. **Predicted email fallback.** For `email` enrichment, if no provider returns an email, the result falls back to a pattern-based **predicted email** built from the contact's name and company `domain`. It is returned with `most_probable_email_status: "predicted"` (and an `emails[]` entry with the same status), alongside `predicted_email`, `email_pattern` (e.g. `firstname.lastname@domain`), and `predicted_email_score`. Predicted emails are **free** — they don't consume enrichment credits. Provide a `domain` to enable this fallback. ## Async vs sync at a glance | Mode | When to use | Tradeoff | | -------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | | **Async (default)** | Production workloads, batches > 5 contacts, anything you can poll | Returns a job ID; you poll `lonescale result ` or pass `--wait`. | | **Async + `--wait`** | Scripts that want a single blocking call | CLI does the polling for you. Hits the `--timeout` if the job takes longer. | | **Sync (`--sync`)** | Single-contact quick checks, exploration | 5 req/min hard rate limit per account; not for batches. | `POST /trigger/enrich` (async) and `POST /trigger/enrich/sync` (sync). # Overview Source: https://docs.lonescale.com/cli/overview The **LoneScale CLI** (`lonescale`) is the official command-line client for the LoneScale Public API. Use it to enrich contacts, source contacts by persona, look up companies, and poll async jobs — all from your terminal or a shell script. It's a thin wrapper around the same REST endpoints documented under [API Reference](/authentication), with terminal-friendly output, file/stdin/inline input, and built-in polling. ## Install ```bash theme={null} npm install -g @lonescale/cli ``` Or run without installing: ```bash theme={null} npx @lonescale/cli --help ``` Requires Node.js ≥ 18. ## Authenticate Get your API key from **Dashboard → Integrations → Public API** and provide it in one of three ways (checked in this order): ```bash theme={null} lonescale enrich --api-key ls_xxx ... ``` ```bash theme={null} export LONESCALE_API_KEY=ls_xxx lonescale enrich ... ``` ```bash theme={null} mkdir -p ~/.lonescale echo '{ "api_key": "ls_xxx" }' > ~/.lonescale/config.json ``` ## Commands | Command | Purpose | | ----------------------------------------------------- | -------------------------------------------------------------- | | [`lonescale enrich`](/cli/enrich) | Waterfall enrichment (email / phone) for a list of contacts | | [`lonescale source`](/cli/source) | Source contacts at a company by persona, seniority, location | | [`lonescale companies search`](/cli/companies-search) | Look up a single company by name, domain, LinkedIn slug, or ID | | [`lonescale result`](/cli/result) | Poll the status / result of an async job | ## Global options These flags work with every command: | Option | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------- | | `--api-key ` | LoneScale API key (overrides env + config file) | | `--output ` | `json` (default when piped), `table` (default in TTY), or `minimal` (just the id/status — script-friendly) | | `--debug` | Print HTTP requests / responses to stderr. API keys are redacted automatically. | ## Output formats Data goes to **stdout**, logs go to **stderr** — so you can pipe results cleanly into `jq`, `xargs`, `tee`, etc. ```bash theme={null} # Count returned contacts lonescale source --domain acme.com --personas p.json --wait --output json \ | jq '.contacts | length' # Capture just the job ID JOB_ID=$(lonescale enrich --type email --contacts c.json --output minimal) ``` See [Recipes](/cli/recipes) for more scripting patterns. # Recipes Source: https://docs.lonescale.com/cli/recipes Real-world scripting patterns that combine the CLI commands. All examples assume `LONESCALE_API_KEY` is already exported (see [Overview → Authenticate](/cli/overview#authenticate)). ## Fire and poll The async pattern, broken into two explicit steps: ```bash theme={null} JOB_ID=$(lonescale enrich --type email --contacts contacts.json --output minimal) echo "Started job $JOB_ID" lonescale result "$JOB_ID" --poll --output json > results.json ``` Or condensed into a single blocking call: ```bash theme={null} lonescale enrich --type email --contacts contacts.json --wait --output json > results.json ``` ## Batch enrichment across multiple files ```bash theme={null} mkdir -p results for file in contacts/*.json; do echo "Processing $file..." lonescale enrich --type email --contacts "$file" --wait --output json \ > "results/$(basename "$file")" done ``` ## Pipe sourcing → enrichment Source contacts at a company, then immediately enrich the LinkedIn URLs: ```bash theme={null} lonescale source --domain figma.com \ --personas-inline '[{"name":"Eng","job_titles":["engineering manager"]}]' \ --locations US --limit 10 --wait --output json \ | jq '[.contacts[] | { firstname, lastname, linkedin_url }]' \ | lonescale enrich --type email --wait --output json \ > figma-eng-emails.json ``` ## Filter with `jq` ```bash theme={null} # Just the names + most probable email lonescale source --domain acme.com \ --personas-inline '[{"name":"Sales","job_titles":["VP Sales","Head of Sales"]}]' \ --wait --output json \ | jq '.contacts[] | { name: (.firstname + " " + .lastname), email: .most_probable_email }' ``` ## Look up a list of companies ```bash theme={null} # domains.txt — one domain per line while read domain; do lonescale companies search --domain "$domain" --output json done < domains.txt | jq -s '.' # combine into a single JSON array ``` ## Debug mode Pass `--debug` to print every HTTP request and response to stderr. API keys are redacted automatically. ``` $ lonescale enrich --type email --contacts c.json --debug --wait [DEBUG] POST https://public-api.lonescale.com/trigger/enrich [DEBUG] Body: { "enrichment_type": ["email"], "contacts": [...] } [DEBUG] Response: 200 { "id": "abc-123", "status": "..." } [INFO] Job abc-123 started, polling... [DEBUG] GET https://public-api.lonescale.com/trigger/run/abc-123 [DEBUG] Response: 200 { "status": "processing" } [DEBUG] GET https://public-api.lonescale.com/trigger/run/abc-123 [DEBUG] Response: 200 { "status": "finished", "contacts": [...] } ``` Because logs go to stderr and data goes to stdout, you can debug a pipeline without polluting the JSON output: ```bash theme={null} lonescale enrich --type email --contacts c.json --debug --wait --output json 2> debug.log | jq '.contacts | length' ``` ## CI / cron usage ```yaml theme={null} # Example: nightly enrichment in GitHub Actions - name: Enrich new leads env: LONESCALE_API_KEY: ${{ secrets.LONESCALE_API_KEY }} run: | npx -y @lonescale/cli enrich \ --type email,phone \ --contacts ./pipeline/new-leads.json \ --wait --output json > ./pipeline/enriched.json ``` Tell us your use case at [happy@lonescale.com](mailto:happy@lonescale.com). # lonescale result Source: https://docs.lonescale.com/cli/result Retrieve the status and result of an async job started by [`lonescale enrich`](/cli/enrich) or [`lonescale source`](/cli/source). ## Synopsis ```bash theme={null} lonescale result [--poll] [--interval ] [--timeout ] ``` ## Examples ```bash theme={null} # One-shot status check lonescale result abc-123 # Poll every 5 seconds until the job finishes, max 3 minutes lonescale result abc-123 --poll --interval 5 --timeout 180 # Capture only the final JSON for further processing lonescale result abc-123 --poll --output json > results.json ``` ## Options | Option | Description | | ---------------------- | ------------------------------------------------------------------- | | `` | **Required.** The job ID returned by `enrich` or `source`. | | `--poll` | Keep polling until the job finishes (or until `--timeout` expires). | | `--interval ` | Seconds between polls (default 3). | | `--timeout ` | Max wait time before giving up (default 120). | Plus all [global options](/cli/overview#global-options). ## Exit codes | Code | Meaning | | ---- | ------------------------------------------------------------------------------------- | | `0` | Job finished successfully (or status was returned without `--poll`). | | `1` | Job failed, timed out, or the CLI couldn't reach the API. Inspect stderr for details. | This means you can chain `lonescale result` safely in shell scripts: ```bash theme={null} JOB_ID=$(lonescale enrich --type email --contacts c.json --output minimal) if lonescale result "$JOB_ID" --poll --output json > results.json; then echo "Enrichment ready in results.json" else echo "Job failed — check the logs" fi ``` # lonescale source Source: https://docs.lonescale.com/cli/source Source contacts at a company by persona, seniority level, and location. Like [`enrich`](/cli/enrich), it is async by default — pass `--wait` to block until the job completes, or call [`lonescale result`](/cli/result) with the returned job ID. ## Synopsis ```bash theme={null} lonescale source --domain [--personas | --personas-inline ] [options] ``` ## Examples ```bash theme={null} # Basic — VP+ at acme.com, all locations lonescale source --domain acme.com \ --personas-inline '[{"name":"Leadership","job_titles":["VP","Head","Director"]}]' \ --seniority vp,head,director \ --wait # Multi-filter with US/UK only, capped at 50 results lonescale source \ --domain acme.com \ --company-name "Acme Inc" \ --company-linkedin "https://linkedin.com/company/acme" \ --personas personas.json \ --locations US,GB \ --seniority c-suite,vp,director \ --limit 50 \ --wait ``` ## Options | Option | Description | | -------------------------- | --------------------------------------------------------- | | `--domain ` | **Required.** Company website domain (e.g. `acme.com`). | | `--personas ` | Path to a JSON file with the personas array. | | `--personas-inline ` | Inline personas array. | | `--company-name ` | Company name. Helpful when the domain is ambiguous. | | `--company-linkedin ` | Company LinkedIn URL. Boosts accuracy by \~25 %. | | `--locations ` | Comma-separated ISO country codes (see list below). | | `--seniority ` | Comma-separated seniority levels (see list below). | | `--limit ` | Max contacts to return. | | `--disable-company-info` | Skip the company enrichment block in the response. | | `--custom ` | Custom metadata JSON, echoed back. | | `--wait` | Poll until the job completes (timeout 120 s by default). | | `--sync` | Use the synchronous endpoint (rate-limited to 5 req/min). | | `--timeout ` | Override the `--wait` / `--sync` timeout. | Plus all [global options](/cli/overview#global-options). ## Persona schema ```json theme={null} [ { "name": "Engineering Leaders", "job_titles": ["CTO", "VP Engineering", "Head of Engineering"], "exclude_job_titles": ["Junior", "Intern"] } ] ``` `job_titles` is matched as **partial / fuzzy** — `"VP Engineering"` will pick up `"VP, Engineering"` and `"VP of Engineering"`. Use `exclude_job_titles` to filter out look-alikes. ## Allowed seniority levels `owner`, `founder`, `c-suite`, `partner`, `vp`, `head`, `director`, `manager`, `senior`, `entry`, `intern` ## Allowed location codes `AT`, `AU`, `BE`, `CA`, `CH`, `DE`, `DK`, `ES`, `FI`, `FR`, `GB`, `IE`, `IL`, `IT`, `LU`, `NL`, `NO`, `PT`, `SE`, `US` `POST /trigger/source-contact` (async) and `POST /trigger/source-contact/sync` (sync). # Connect to Claude Source: https://docs.lonescale.com/mcp/connect The LoneScale MCP server can be added to Claude as a **custom connector**. Connection uses OAuth 2.0 — Claude will pop up a LoneScale consent screen where you paste your API key, then Claude stores the access token and reuses it for every tool call. ## Prerequisites * A LoneScale API key — copy it from **Dashboard → Integrations → Public API**. * A Claude account on a plan that supports custom connectors (currently Claude.ai web + Claude Desktop). ## Add the connector In **Claude.ai** → **Settings → Connectors** (or **Claude Desktop → Settings → Connectors**), click **Add custom connector**. Server URL: ``` https://mcp-enrich.lonescale.com/mcp ``` Name it `LoneScale` (or whatever you'd like to see in the tool picker). Claude redirects you to the LoneScale consent screen. Paste your API key and click **Authorize**. You'll be sent back to Claude, and the connector status flips to **Connected**. Start a new chat and ask Claude something like *"Search LoneScale for the company at stripe.com"*. You should see Claude call `public-api-companies-search` and return the result. Head to [Examples](/mcp/examples) for more. ## OAuth flow under the hood For client implementers (or anyone curious), here's what happens during the consent flow: 1. Claude discovers the server's auth metadata via: * `GET /.well-known/oauth-protected-resource` (RFC 9728) * `GET /.well-known/oauth-authorization-server` (RFC 8414) 2. Claude **dynamically registers** itself via `POST /register` (RFC 7591). It receives a `client_id` scoped to its `redirect_uri` (`https://claude.com/api/mcp/auth_callback`). 3. Claude opens `/authorize?client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256` in your browser. You see the LoneScale consent screen and paste your API key. 4. LoneScale verifies the API key, issues a one-time `code`, and redirects back to Claude with `?code=…&state=…`. 5. Claude exchanges the code at `POST /token` with the PKCE `code_verifier`. The response contains an `access_token` that Claude attaches as `Authorization: Bearer …` to every subsequent `/mcp` call. ## Disconnect To revoke access, open **Settings → Connectors** in Claude and click **Disconnect** on the LoneScale row. Your API key itself stays valid — rotate it in the LoneScale dashboard if you want to invalidate it everywhere. ## Other clients The same server works with any MCP-aware client that supports Streamable HTTP + OAuth, including: * [Cursor](https://docs.cursor.com/context/mcp) — add it under **Settings → MCP → Add custom server**. * Custom agents built with the [Anthropic SDK](https://docs.anthropic.com/) via the `mcp-remote` adapter. ## Troubleshooting | Symptom | Likely cause | Fix | | ---------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` after authorizing | API key was rotated or revoked | Reconnect with a fresh key | | Consent screen says "Unknown client\_id" | Stale connector entry | Remove the connector and add it again | | Tool calls hang on async jobs | Webhook delay | Claude will poll `get-job-result`; results usually arrive within 60s for enrichment, a few minutes for sourcing | | Need help | — | Email [happy@lonescale.com](mailto:happy@lonescale.com) | # Examples Source: https://docs.lonescale.com/mcp/examples Three prompts you can paste straight into a Claude chat once the LoneScale connector is connected. Each shows the tool Claude is expected to pick and the rough shape of the answer. ## 1. Look up a company > *"Look up the company at **stripe.com** on LoneScale — I want the name, industry, headcount, and LinkedIn URL."* **What Claude does:** calls `public-api-companies-search` with `domain: "stripe.com"`. **Sample response:** ```json theme={null} { "linkedinId": "stripe", "linkedinUrl": "https://www.linkedin.com/company/stripe", "name": "Stripe", "domain": "stripe.com", "industry": "Financial Services", "staffCount": 8000, "headcount": { "engineering": 2400, "sales": 600, ... } } ``` Add **"and break headcount down by function"** to your prompt and Claude will set `enrich: true`, which returns a function-level breakdown. ## 2. Enrich a list of contacts with email + phone > \_"Here are 3 LinkedIn URLs: > > * [https://www.linkedin.com/in/patrickcollison](https://www.linkedin.com/in/patrickcollison) > * [https://www.linkedin.com/in/johncollison](https://www.linkedin.com/in/johncollison) > * [https://www.linkedin.com/in/cristinacordova](https://www.linkedin.com/in/cristinacordova) > > Enrich each one with email and phone using LoneScale."\_ **What Claude does:** 1. Calls `public-api-enrich` with `enrichment_type: ["email", "phone"]` and the 3 contacts. Gets back a `lonescale_job_id`. 2. Polls `get-job-result` every few seconds until the status flips from `pending` to `ready`. 3. Shows you a table with the enriched email + phone for each contact. Enrichment is **waterfall** — LoneScale tries multiple providers and returns the first hit per contact. Most jobs finish in under 60 seconds. When no provider returns an email, the result falls back to a free **predicted email** built from the contact's name and company domain, flagged with `most_probable_email_status: "predicted"` and accompanied by `predicted_email`, `email_pattern`, and `predicted_email_score`. Predicted emails don't consume credits. ## 3. Source contacts at a company > *"Find 10 engineering managers at **figma.com** based in the US, and show me their LinkedIn URLs and current titles."* **What Claude does:** 1. Calls `public-api-contact-sourcing` with `company_domain: "figma.com"`, `personas: ["engineering manager"]`, `included_locations: ["United States"]`, `limit: 10`. Gets back a `lonescale_job_id`. 2. Polls `get-job-result` until the job is `ready` (sourcing typically takes 1–3 minutes). 3. Returns the list of sourced contacts inline. Chain it with prompt 2 ("…now enrich those 10 contacts with email") and Claude will pipe the LinkedIn URLs from the sourcing result straight into `public-api-enrich`. ## Tips * **Be explicit about LoneScale.** Mentioning "LoneScale" in the prompt makes Claude pick the right connector when you have several connected. * **Don't interrupt async jobs.** While Claude is polling `get-job-result`, don't send a new message — it will lose the `lonescale_job_id` and have to restart. * **Custom metadata.** All tools accept an optional `custom` map that's echoed back in webhook payloads. Useful when you're chaining LoneScale with a CRM update further downstream. # Overview Source: https://docs.lonescale.com/mcp/overview The **LoneScale MCP server** lets AI assistants (Claude, Cursor, ChatGPT, custom agents) call LoneScale's enrichment and sourcing capabilities as tools — directly from a chat, no glue code required. It implements the [Model Context Protocol](https://modelcontextprotocol.io) over Streamable HTTP and authenticates users via OAuth 2.0 with PKCE. Behind the scenes, every tool call uses the same LoneScale Public API documented elsewhere on this site. ## Endpoint ``` https://mcp-enrich.lonescale.com/mcp ``` Transport: **Streamable HTTP** (MCP spec ≥ 2025-06-18). Auth: **OAuth 2.0** (user-consent flow — see [Connect to Claude](/mcp/connect)). ## Available tools The tools below are available once the user has authorized with their LoneScale API key. | Tool | Mode | Description | | ----------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `whoami` | sync | The user behind the API key: name, remaining credits, and `crm_links` (the HubSpot/Salesforce user they are linked to, or `null`). Tells whether a CRM push will be assigned to them or to the default owner. | | `public-api-companies-search` | sync | Look up a company by name, domain, LinkedIn slug, or LinkedIn ID. Returns one fixed-shape company profile. | | `public-api-crm-search` | sync | Look up a contact in the connected CRM(s) by email, LinkedIn URL, or name + company. Returns matches with owner and `owned_by_you`, plus `caller_crm_links`. | | `public-api-enrich` | async | Trigger waterfall enrichment (email and/or phone) for a list of contacts. Checks the CRM first and stops if the contact already exists. With `sync_to_crm: true` the enriched contact is written to the CRM and assigned to the caller's linked CRM user. Returns a `lonescale_job_id`. | | `public-api-contact-sourcing` | async | Source contacts at a company by persona, seniority, and location. Returns a `lonescale_job_id`. | | `get-job-result` | sync | Poll for results of an async job using its `lonescale_job_id`. Returns `pending` until status is `ready`. Sourced contacts come back with a `crm` block (existing record + owner, or not in CRM). | Sync tools return results inline. Async tools return a job id immediately and the assistant must call `get-job-result` every few seconds until the status is `ready`. ## What you'll need * A LoneScale account with an API key (Dashboard → Integrations → Public API). * A client that supports MCP custom connectors — for example [Claude](https://claude.ai), [Cursor](https://cursor.sh), or any agent built with the Anthropic SDK. Step-by-step setup, including the OAuth consent screen. Three concrete prompts you can copy-paste into Claude.