MCP Server
Connect Claude, Cursor, and other Model Context Protocol clients directly to Oppora. 51 tools spanning the full outbound pipeline — discovery, bulk upsert, contact resolution, AI enrichment & scoring, async tasks, workflows, campaigns, research.
https://api.oppora.ai/mcpStreamable HTTP transport. Authenticate with ?api_key=…, X-API-Key, or Authorization: Bearer.
Quick start
60 secondsGenerate a key
In Oppora, open Integrations → Oppora API and click Create API key. Copy the opp_live_… secret (shown once).
Open IntegrationsAdd the connector
Pick your MCP client below and paste the connection URL. Both query-string and Authorization-header auth work.
Try a tool
Ask the assistant "what lists do I have?" — it calls my_lists. Then try discover_companies + add_companies_to_list.
Connect a client
Same endpoint, same keyPro/Max plans: Settings → Customize → Connectors → + → Add custom connector. Paste the URL below:
https://api.oppora.ai/mcp?api_key=YOUR_KEY
Team/Enterprise: Owner adds via Organization settings → Connectors → Add → Custom → Web, then members enable under Customize.
Anthropic's servers (not your browser) connect to this URL, so it must be reachable from the public internet. localhost won't work.
Tools reference
51 totalAll tools return TOON-encoded text except discover_companies and discover_people (JSON). Filter below to jump to a tool.
Discovery (companies & people)
2Search Oppora's global databases. Both auto-save results into Oppora's DB so every returned row carries an Oppora `id` you can pass straight to the add-to-list tools. 1 data credit per non-empty call.
discover_companiesSearch the global company DB by industry, size, location, revenue, funding, growth. Auto-saved.1 data credit
discover_companiesSearch the global company DB by industry, size, location, revenue, funding, growth. Auto-saved.industry?: string[], size?: string[] (bucket), hq_country?: string[] (ISO 3-alpha), location?: string[], company_type?: string[], year_founded?: string, revenue_range_min/max?: int (USD), last_funding_round_type?: string[], employee_growth_6m_min?: int (percent), exclude_*?: string[], limit?: int, next_cursor?: string
JSON `{ results: [{id, name, domain, …}], next_cursor, has_more, count, credit_charged, credits_remaining }`. `id` is Oppora's saved Company.id — reuse it directly.
discover_companies( hq_country=["USA"], industry=["Software Development"], size=["51-200","201-500"], revenue_range_min=5_000_000, limit=25, )
discover_peopleSearch the global people DB by title, dept, mgmt level, company, skills, location. Auto-saved with `id`.1 data credit
discover_peopleSearch the global people DB by title, dept, mgmt level, company, skills, location. Auto-saved with `id`.title?: string, departments?: string[], management_levels?: string[], company_name?: string|string[], company_industry?: string, employee_count?: string (bucket), years_experience?: string ("Less than 1 year"|"1 to 2 years"|"3 to 5 years"|"6 to 10 years"|"More than 10 years"), location?: string, skills?: string[], limit?: intJSON `{ results: [{id, first_name, last_name, …}], credit_charged, credits_remaining }`
discover_people( title="VP Sales", departments=["Sales"], management_levels=["VP","Director"], company_name=["Stripe","Plaid"], limit=25, )
Filter helpers
7Resolve exact valid values before calling discover_* or find_leads_for_companies. Wrong values match nothing — these are essential.
get_available_industries~435 LinkedIn industry names.Free
get_available_industries~435 LinkedIn industry names.get_available_countriesCountry names + ISO 3-alpha codes. Pass `iso_alpha3` to `hq_country`.Free
get_available_countriesCountry names + ISO 3-alpha codes. Pass `iso_alpha3` to `hq_country`.get_available_company_typesPrivately Held, Public Company, Nonprofit, etc.Free
get_available_company_typesPrivately Held, Public Company, Nonprofit, etc.get_available_last_funding_round_typesseed, series_a, series_b, etc.Free
get_available_last_funding_round_typesseed, series_a, series_b, etc.get_available_departmentsFor `discover_people.departments`.Free
get_available_departmentsFor `discover_people.departments`.get_available_management_levelsFor `discover_people.management_levels`.Free
get_available_management_levelsFor `discover_people.management_levels`.search_locationsAutocomplete city/region names for the `location` filter.Free
search_locationsAutocomplete city/region names for the `location` filter.Saved companies & leads
8Manage records the user has already saved into Oppora lists.
my_listsUser's lists with company + lead counts. Call this BEFORE add/remove tools.Free
my_listsUser's lists with company + lead counts. Call this BEFORE add/remove tools.create_listCreate a new list or return the existing one with that name (idempotent, per-user-per-org dedup).Free
create_listCreate a new list or return the existing one with that name (idempotent, per-user-per-org dedup).name: string
TOON `{ id, name, is_existing, message, list_url }`
create_list(name="Top Prospects Q2")
list_my_saved_companiesSaved companies (optionally scoped to one list).Free
list_my_saved_companiesSaved companies (optionally scoped to one list).list_my_saved_leadsSaved leads (ListLeadContact rows).Free
list_my_saved_leadsSaved leads (ListLeadContact rows).add_companies_to_listAdd EXISTING Oppora company IDs to a list. Use after discover_companies / create_companies_bulk.1 data credit per added
add_companies_to_listAdd EXISTING Oppora company IDs to a list. Use after discover_companies / create_companies_bulk.remove_companies_from_listBulk-remove company memberships.Free
remove_companies_from_listBulk-remove company memberships.add_leads_to_listAdd EXISTING Oppora lead IDs to a list. Use after discover_people / create_leads_bulk.1 data credit per added
add_leads_to_listAdd EXISTING Oppora lead IDs to a list. Use after discover_people / create_leads_bulk.remove_leads_from_listBulk-remove lead memberships.Free
remove_leads_from_listBulk-remove lead memberships.Bulk upsert (external data → Oppora DB)
2For data from web scrapes, news search, user input, CSV imports — anything NOT from discover_*. Both tools dedup against the global DB and optionally add to a list in one call. Free — credits hit at discover/enrich time.
create_companies_bulkUpsert ≤100 company dicts. Dedups by name / linkedin / website / domain. Returns Oppora IDs.Free
create_companies_bulkUpsert ≤100 company dicts. Dedups by name / linkedin / website / domain. Returns Oppora IDs.companies: list[{name (required), domain?, website?, linkedin?, industry?, approx_employee_size?, meta?}], list_id?: intTOON `{ company_ids, added_to_list, list_id, list_url, message }`
create_companies_bulk(
companies=[
{"name": "Acme", "domain": "acme.com"},
{"name": "Globex", "linkedin": "https://linkedin.com/company/globex"},
],
list_id=12, # optional — save + add to list in one call
)create_leads_bulkUpsert ≤100 leads. Dedup: email → linkedin → first_name+last_name+company. Auto-creates employer Company from company_name.Free
create_leads_bulkUpsert ≤100 leads. Dedup: email → linkedin → first_name+last_name+company. Auto-creates employer Company from company_name.leads: list[{first_name OR email (one required), last_name?, linkedin?, title?, phone?, location?, company_name?, company_domain?, company_linkedin?, company_industry?}], list_id?: intTOON `{ created, existing, failed, lead_ids, failed_details, list_id, message }`
create_leads_bulk(
leads=[
{"first_name": "Alice", "email": "[email protected]", "company_name": "Stripe"},
{"linkedin": "https://linkedin.com/in/bob-smith"},
],
list_id=12,
)Async list operations
4Long-running per-list operations. Each returns a task_id immediately; the work runs on Celery. Every response carries an oppora_url so the user can open the affected list.
enrich_companiesAI enrichment for companies in a list (website, industry, size, description, etc.).1 data credit per enriched company
enrich_companiesAI enrichment for companies in a list (website, industry, size, description, etc.).list_id: int (required), company_ids?: int[] (omit = whole list)
TOON `{ task_id, message, list_id, list_name, counts, oppora_url }`
enrich_companies(list_id=12) # enrich every company in the list
find_leads_for_companiesFind people at companies in a list. Requires departments + management_level — resolve via get_available_*. Provide a comprehensive title list.1 / 3 / 5 credits per saved lead (Standard / Premium / Deep)
find_leads_for_companiesFind people at companies in a list. Requires departments + management_level — resolve via get_available_*. Provide a comprehensive title list.list_id: int, departments: string[], management_level: string[], company_ids?: int[], title?: string[], lead_limit?: int (≤1000), country?/country_full_name?/location?/experience?/employee_count? string[], source_tiers?: ["standard"|"premium"|"deep"], exclude_location?: string[], main_objective?: string
TOON `{ task_id, message, list_id, list_name, oppora_url }`
find_leads_for_companies( list_id=12, departments=["sales", "business_development"], management_level=["vp", "director"], title=["AE", "Account Executive", "BDR", "SDR"], lead_limit=100, source_tiers=["standard", "premium"], )
score_leadsRank every lead in a list against the user's offer/ICP. Returns "Yes" / "No" + reason per lead.AI credits per scored lead
score_leadsRank every lead in a list against the user's offer/ICP. Returns "Yes" / "No" + reason per lead.list_id: int, service_type: string, leads_per_company: int, priority_departments: string, specific_leads_per_department: string, additional_requirements?: string
TOON `{ task_id, message, list_id, list_name, counts, oppora_url }`
score_leads( list_id=12, service_type="B2B sales-automation SaaS for outbound teams", leads_per_company=3, priority_departments="Sales, Marketing", specific_leads_per_department="VP Sales x2, Director of Marketing x1", )
find_emails_for_leadsBulk-resolve work emails for leads in a list. For one-off, use email_search.1 contact credit per email found (misses free)
find_emails_for_leadsBulk-resolve work emails for leads in a list. For one-off, use email_search.list_id: int, lead_ids?: int[] (omit = whole list)
TOON `{ task_id, message, list_id, list_name, counts, oppora_url }`
find_emails_for_leads(list_id=12)
Tasks (poll async work)
2Read the status of any task created by the async tools above. Every row carries an oppora_url for the affected list/workflow.
get_task_statusFull snapshot: status, progress %, summary, credits used, oppora_url. Cross-user isolated.Free
get_task_statusFull snapshot: status, progress %, summary, credits used, oppora_url. Cross-user isolated.task_id: int
TOON `{ task_id, title, status, progress, summary, credits_used, oppora_url, … }`
get_task_status(task_id=1408)
list_my_tasksPaginated list of the user's tasks. Filter by status.Free
list_my_tasksPaginated list of the user's tasks. Filter by status.status?: "pending"|"in_progress"|"completed"|"stopped", page?: int, limit?: int (≤100)
TOON `{ total, page, limit, has_more, tasks[N]{task_id, title, status, progress, summary, oppora_url, …} }`
list_my_tasks(status="in_progress", limit=10)
Workflows
3Chain operations (enrich → lead-find → score → email-find → campaign) into a workflow tied to a list. Run on-demand or on a schedule.
create_workflowCreate a workflow on a list. Optional one-shot schedule (any IANA timezone).Free
create_workflowCreate a workflow on a list. Optional one-shot schedule (any IANA timezone).name: string, list_id: int, scheduled_time?: "YYYY-MM-DD HH:MM", timezone_str?: string
TOON `{ workflow_id, message, oppora_url }`
create_workflow(name="Weekly outbound", list_id=12)
add_workflow_nodeAdd ONE agent node to a workflow. Types: company-crawl, ai-enrich, company-filter, lead-search, ai-recommendation, search-contact, verify-contact, generate-campaign.Free
add_workflow_nodeAdd ONE agent node to a workflow. Types: company-crawl, ai-enrich, company-filter, lead-search, ai-recommendation, search-contact, verify-contact, generate-campaign.workflow_id: int, list_id: int, prompt: string (node type + any instructions)
TOON `{ workflow_id, message, oppora_url }`
add_workflow_node(workflow_id=79, list_id=12, prompt="ai-enrich")
start_workflowTrigger a workflow to run now. Nodes execute in sequence on Celery; poll each via list_my_tasks.Free (nodes charge their own credits)
start_workflowTrigger a workflow to run now. Nodes execute in sequence on Celery; poll each via list_my_tasks.workflow_id: int, list_id: int
TOON `{ message, oppora_url }`
start_workflow(workflow_id=79, list_id=12)
Contact resolution
3Single-shot finders + verifier. Credits charged ONLY on success.
email_searchFind email by (first_name + last_name | full_name) + domain. Domain is required.1 contact credit on success
email_searchFind email by (first_name + last_name | full_name) + domain. Domain is required.email_search( first_name="Alice", last_name="Chen", domain="stripe.com", )
email_verifierStandard SMTP check or advanced (catch-all aware).1 verify credit on success
email_verifierStandard SMTP check or advanced (catch-all aware).email_verifier(email="[email protected]", mode="standard")
phone_searchFind phone via linkedin_url (best) OR name + company/domain. Checks Oppora DB cache first.1 phone credit on success — DB-cache hits are free
phone_searchFind phone via linkedin_url (best) OR name + company/domain. Checks Oppora DB cache first.phone_search(linkedin_url="https://linkedin.com/in/alice-chen-vp")
Campaigns
8Inspect, set up, update, start, and validate campaigns.
list_campaignsList with sent/opened/clicked/replied + rates.Free
list_campaignsList with sent/opened/clicked/replied + rates.get_campaignFull config: lists, accounts, templates, schedule, stats.Free
get_campaignFull config: lists, accounts, templates, schedule, stats.get_campaign_reportFunnel report (date range).Free
get_campaign_reportFunnel report (date range).create_or_update_campaignFull setup — attach lists, templates, schedule, accounts, dates, flags.Free
create_or_update_campaignFull setup — attach lists, templates, schedule, accounts, dates, flags.update_campaign_statusPause / activate / complete / draft.Free
update_campaign_statusPause / activate / complete / draft.start_campaignActivate; refuses if not ready (returns issue list).Free
start_campaignActivate; refuses if not ready (returns issue list).check_campaign_readinessValidate setup; returns clickable URLs to fix each missing component.Free
check_campaign_readinessValidate setup; returns clickable URLs to fix each missing component.list_email_accountsConnected Gmail/Outlook/SMTP accounts with status.Free
list_email_accountsConnected Gmail/Outlook/SMTP accounts with status.Templates & schedules
7Reusable building blocks for campaigns.
list_templatesEmail + LinkedIn templates (filter by kind).Free
list_templatesEmail + LinkedIn templates (filter by kind).list_follow_up_templatesFollow-ups ordered by `order` + `after_days`.Free
list_follow_up_templatesFollow-ups ordered by `order` + `after_days`.list_schedulesSending schedules with timezone + days.Free
list_schedulesSending schedules with timezone + days.create_email_templateHTML body with personalisation variables.Free
create_email_templateHTML body with personalisation variables.create_linkedin_templatePlain text, ≤200 chars.Free
create_linkedin_templatePlain text, ≤200 chars.create_follow_up_template`order` + `after_days` + optional subject.Free
create_follow_up_template`order` + `after_days` + optional subject.create_scheduleAny pytz/IANA timezone accepted.Free
create_scheduleAny pytz/IANA timezone accepted.Research / web
5External web/news/maps/AI search. Free for web_search + web_scrape; 1 AI credit on success for the rest.
web_searchGoogle-style web search. Titles, snippets, links, knowledge graph.Free
web_searchGoogle-style web search. Titles, snippets, links, knowledge graph.web_scrapeFetch + extract readable text from a URL.Free
web_scrapeFetch + extract readable text from a URL.smart_web_searchAI semantic search for niche queries.1 AI credit on success
smart_web_searchAI semantic search for niche queries.local_business_searchGoogle Maps (rating, phone, website).1 AI credit on success
local_business_searchGoogle Maps (rating, phone, website).news_searchLatest news (good for intent signals).1 AI credit on success
news_searchLatest news (good for intent signals).End-to-end example
discover → enrich → score → outreachA realistic flow the AI agent can run in a single conversation. Async steps return a task_id; poll progress with get_task_status or list_my_tasks.
- 1
get_available_industries + get_available_countries— resolve exact values - 2
discover_companies(industry=[…], hq_country=[…])— auto-saved; every row has Oppora id - 3
my_lists— pick or ask user to create a list - 4
add_companies_to_list(list_id, company_ids=[…])— pin them - 5
enrich_companies(list_id)— async; returns task_id - 6
find_leads_for_companies(list_id, departments=[…], management_level=[…], title=[…])— async lead search - 7
score_leads(list_id, service_type, leads_per_company, …)— AI ranking - 8
find_emails_for_leads(list_id)— bulk email resolution - 9
get_task_status(task_id) / list_my_tasks(status="completed")— progress + oppora_url links - 10
create_email_template + create_schedule + list_email_accounts - 11
create_or_update_campaign(…) → check_campaign_readiness → start_campaign
create_workflow + a sequence of add_workflow_node calls (ai-enrich, lead-search, ai-recommendation, search-contact, generate-campaign) → start_workflow. For external data (web scrapes, news, user input): create_companies_bulk / create_leads_bulk upserts in one shot.FAQ
What format do tool responses use?
discover_companies and discover_people, which return JSON because their results carry nested fields that benefit from a real object structure.When am I charged credits?
Are MCP and REST API the same key?
opp_live_… key authenticates both. Generate keys under Integrations → Oppora API.Do I need a paid plan?
Why TOON instead of JSON?
results[N]{f1,f2}:) + CSV rows is ~40% fewer tokens than JSON for uniform record arrays — the typical shape these tools return. Less tokens = lower LLM cost + faster response.How do I validate a key works?
https://api.oppora.ai/mcp?api_key=YOUR_KEY and verify the tools list appears.