BetterContact MCP server for AI agents

Live

API KEY

CONTACT ENRICHMENT

CRM & Sales

BetterContact gives your agent contact enrichment: run waterfall enrichment for work emails and mobile phones, look up person and company profiles, and search for new leads and companies.

  • Per-user credentials: each call uses the actual user's API key, never a shared bot.
  • Encrypted per-tenant vault: AES-256, resolved at request time, never in LLM context.
  • Scoped before every call: pre-call scope check, 90-day SIEM-exportable audit chain.
BetterContact
agent · Acme Q3
Run
Are the results ready for this morning's enrichment request?
S
bettercontact_get_enrichment_results
108ms
Prospecting agent
The request is complete. 21 of 25 leads have a work email and 12 have a mobile phone.
Sources: BetterContact, 1 enrichment request
bettercontact
25 leads
18:29
Message Claude...

Tools your prospecting agent reaches for on BetterContact, scoped per user.

CALL ANY TOOL
9 tools for contact enrichment in BetterContact: submit leads for waterfall email and phone enrichment, enrich person and company profiles, and run Lead Finder and Company Finder searches.
bettercontact_create_company_finder_search
Search create company finder
Start an asynchronous search for companies by firmographic, funding, and hiring criteria. Returns a request_id. The companies are not in this response. Use get_company_finder_results with the request_id to fetch companies. Use create_lead_finder_search to find people. Use enrich_company_profile for a single known company.
Parameters
Name
Type
Required
Description
filters
object
Required
Search criteria as an object. At least one filter must hold a value. List filters take only an include list: company (lowercase domains without a path), company_linkedin_url, company_industry, company_hq_location, company_technologies, company_keywords, company_description, revenue_ranges, last_funding_round_names, job_post_titles, job_posting_countries, job_posting_locations. Number filters are company_headcount_min and company_headcount_max. Boolean filters are is_b2b, is_b2c, and is_public. Range filters use gte and lte: last_funding_date_range (YYYY-MM-DD), last_amount_raised_usd, total_amount_raised_usd. Industry, technology, revenue range, and funding round values must match the vendor's fixed lists exactly. Example: {"company_industry": {"include": ["Software Development"]}, "company_hq_location": {"include": ["United States"]}, "company_headcount_min": 51, "company_headcount_max": 500, "is_b2b": true}.
limit
integer
Optional
Number of companies to return for this page, between 1 and 200. Defaults to 100 when omitted; a value above 200 is refused. Example: 25.
offset
integer
Optional
Number of matches to skip. To page, keep limit constant and increase offset by limit each call; the total match count is in the results summary. Example: 25.
bettercontact_create_lead_finder_search
Search create lead finder
bettercontact_enrich_company_profile
Enrich company profile
bettercontact_enrich_lead_profile
Enrich lead profile
bettercontact_enrich_leads
Enrich leads
bettercontact_get_company_finder_results
Get company finder results
bettercontact_get_credits_balance
Get credits balance
bettercontact_get_enrichment_results
Get enrichment results
bettercontact_get_lead_finder_results
Get lead finder results
Build your Agent
Same auth pattern across LangChain, OpenAI, Anthropic, and Google ADK.
Python · LlamaIndex
import { ScalekitClient } from "@scalekit-sdk/node";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);

// BetterContact tools scoped to this user
const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["bettercontact"], toolNames: [
    "bettercontact_enrich_leads",
    "bettercontact_get_enrichment_results",
    "bettercontact_get_credits_balance"] },
  pageSize: 100,
});

const agent = createReactAgent({ llm, tools });
await agent.invoke({ messages: [{ role: "user", content: "Find work emails and mobile phones for these 25 webinar leads" }] });
import OpenAI from "openai";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);
const openai = new OpenAI();

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["bettercontact"] }, pageSize: 100,
});

const res = await openai.chat.completions.create({
  model: "gpt-5",
  messages: [{ role: "user", content: "Find work emails and mobile phones for these 25 webinar leads" }],
  tools,
});

// Execute the tool call with the user's vaulted BetterContact API key
await sk.tools.executeTool(res.choices[0].message.tool_calls[0], "user_123");
import Anthropic from "@anthropic-ai/sdk";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);
const anthropic = new Anthropic();

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["bettercontact"] }, pageSize: 100,
});

const msg = await anthropic.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Find work emails and mobile phones for these 25 webinar leads" }],
  tools,
});

// Tool call runs with the user's vaulted BetterContact API key
await sk.tools.executeTool(msg.content, "user_123");
import { Agent } from "@google/adk/agents";
import { ScalekitClient } from "@scalekit-sdk/node";

const sk = new ScalekitClient(env.SCALEKIT_ENV_URL, env.SCALEKIT_CLIENT_ID, env.SCALEKIT_CLIENT_SECRET);

const { tools } = await sk.tools.listScopedTools("user_123", {
  filter: { connectionNames: ["bettercontact"] }, pageSize: 100,
});

const agent = new Agent({
  name: "prospecting_agent",
  model: "gemini-2.5-pro",
  instruction: "BetterContact tools scoped to this user",
  tools,
});

await agent.run("Find work emails and mobile phones for these 25 webinar leads");
Try these prompts
Copy any prompt into your agent. Each maps directly to a BetterContact tool. Click to copy, paste into your agent, done.
Enrichment
Copy the prompt
Copied
Find work emails and mobile phones for these 25 leads.
Copy the prompt
Copied
Get the results of this morning's enrichment request.
Copy the prompt
Copied
How many BetterContact credits do I have left?
Profiles
Copy the prompt
Copied
Look up the company profile for northwind.com.
Copy the prompt
Copied
Look up Priya Shah at Northwind and return her current role.
Copy the prompt
Copied
Summarize the size and industry of acme.io.
Lead and company search
Copy the prompt
Copied
Find VP of Marketing leads at fintech companies in London.
Copy the prompt
Copied
Find B2B software companies with 50 to 200 employees in Germany.
Copy the prompt
Copied
Get the results of my last Lead Finder search.
SEE HOW AUTH WORKS
Each user connects their own BetterContact API key once, and Scalekit sends it with every call. Keys stay vaulted, every call is scope checked, and every action is logged.
1
Authorize
Your user connects
BetterContact
once. We tie it to their identity and the meetings they approved — no shared bot account, no org-wide access
Who:
user ‘A’
when:
Once per user
access:
Limited to user
2
Store
Their
BetterContact
token lives in a vault scoped to them. User A's meetings are never reachable by an agent acting for user B, even on the same connection
vault:
encrypted
scope:
per-user
tokens:
auto-refreshed
3
Resolve
When your agent calls a
BetterContact
tool, we fetch the right token server-side. It never touches your agent, never appears in the LLM context, never shows up in your logs
speed:
~40ms
check:
before every call
seen by:
nobody
4
Audit
Every
BetterContact
tool call is logged — who triggered it, which meeting was fetched, what came back. 90 days of history, tied to the user who authorized it
history:
90 days
export:
SIEM-ready
logged:
every call
Test other agents
See the same per-user auth pattern across other sales and prospecting connectors.
GTM and RevOps Teams
Outbound prospecting agent
Searches Apollo for prospects matching your ICP, scores and ranks them, drafts personalized outreach in Gmail, and logs every send to Google Sheets. Mail goes out as the rep, not from a shared inbox.
GTM and RevOps Teams
CRM AI agent
Reads the Granola transcript after every call, extracts next steps and updates the HubSpot record, drafts the follow-up in Gmail, and confirms in Slack, all on the rep's own delegated OAuth.
Test other agents
See the same per-user auth pattern across other sales and prospecting connectors.
SALES
Outbound prospecting agent
Search Apollo for ICP matches, rank them, draft personalised Gmail outreach, and log every send to Google Sheets.
GTM
CRM AI agent
Turn each Granola call transcript into a HubSpot record update, a drafted Gmail follow-up, and a Slack recap.
Why Scalekit
Secure your agent's access. Connectors ship in minutes
01.
Shared tokens break per-user analytics
A shared BetterContact API key looks fine in a demo. In production every enrichment request looks like one service account, and you cannot tell which user triggered it. Scalekit resolves the credential of the actual user who triggered the agent, never a shared bot.
// shared key
audit → bot_service_account

// scalekit
audit → user_abc ✓
02.
Authentication is not authorization
03.
Multi-tenancy is architectural
04.
BetterContact today. Ten connectors tomorrow.
“Our agents act across Salesforce, Gong, Google Drive, and more, on behalf of every customer. Scalekit behind the scenes meant we can keep adding tools without ever rebuilding how credentials or tool calling work.”
Venu Madhav Kattagoni
Head of Engineering / Von
FAQs
Frequently Asked Questions
Does the agent access BetterContact as the user or through a shared key?
As the user. Each user connects their own BetterContact API key once, and Scalekit sends it with every call. Credits come from that user's BetterContact account, and audit logs attribute every request to that user, not a shared service account.
Where is the BetterContact API key stored?
In Scalekit's managed AES-256 token vault, namespaced per tenant. To rotate it, the user reconnects with a new API key. Revocation is a single dashboard action. Credentials never appear in prompts, logs, or LLM context.
Can I limit what the agent does in BetterContact?
Yes. Filter by tool name in listScopedTools to expose only what you want, for example the profile lookups without bettercontact_enrich_leads, which submits up to 100 leads at once. Scalekit also enforces scope checks before every API call.
What happens when a user revokes BetterContact access?
The connection is invalidated on the next tool call. Subsequent requests for that user fail closed with a clear error. Other users in the tenant remain unaffected. The event is logged for audit.
How does BetterContact return enrichment results?
Asynchronously for bulk work. bettercontact_enrich_leads submits 1 to 100 leads and returns a request ID, and bettercontact_get_enrichment_results reads the results once they are ready. Lead Finder and Company Finder searches work the same way, while the single person and company profile lookups return right away.
Start in your coding agent
Up and running in one command
Install the Scalekit skill in your editor of choice. Connector, auth, tools, prompt, all wired up
Claude Code REPL
/plugin marketplace add scalekit-inc/claude-code-authstack
/plugin install agentkit@scalekit-auth-stack
Cursor Code REPL
# ~/.cursor/mcp.json
{
""mcpServers"": {
""bettercontact"": {
""url"": ""https://mcp.scalekit.com/bettercontact"",
""headers"": { ""Authorization"": ""Bearer $SCALEKIT_TOKEN"" }
}
}
}
Codex Code REPL
# ~/.codex/config.toml
[mcp_servers.bettercontact]
url = ""https://mcp.scalekit.com/bettercontact""
auth_env = ""SCALEKIT_TOKEN""
Copilot Code REPL
# .vscode/mcp.json
{
""servers"": {
""bettercontact"": {
""url"": ""https://mcp.scalekit.com/bettercontact"",
""type"": ""http""
}
}
}