Skip to content

Developers · MCP

Build on the registry your team already uses.

External agents operate Limena over the Model Context Protocol: the same 103 capabilities Limena’s own assistant uses, with scoped API keys, rate zones, and every mutation written to the audit log. Point an MCP client at the endpoint below and your agent can read and write your CRM, within the scopes you grant it.

MCP endpoint
url
https://api.limena.io/mcp/sseHTTP + Server-Sent Events. A local checkout can also run it over STDIO.
auth
Authorization: Bearer lmn_…
requires
an Ultra workspace + a key with mcp:accessKeys are created on Pro and up; the connection itself is an Ultra capability. The catalogue below is the same on every plan.

Connect

Limena speaks MCP over two transports. Most clients use the hosted SSE endpoint; a local checkout of the server can run over STDIO. This is the same configuration the app shows under Settings → Connect an AI client.

Connecting needs an Ultra workspace and an API key with the mcp:access scope.

Create the key under Settings → API keys (available on Pro and up). Browsing this reference needs no account.

Hosted SSE (recommended)
{
  "mcpServers": {
    "limena": {
      "url": "https://api.limena.io/mcp/sse",
      "headers": {
        "Authorization": "Bearer <YOUR_API_KEY>"
      }
    }
  }
}
Self-hosting / local development (STDIO transport)

STDIO runs the MCP server as a subprocess on your machine; you only need it with a local checkout of the Limena repo. Replace <REPO> with the checkout path and <YOUR_API_KEY> with your lmn_ key.

Self-host (STDIO)
{
  "mcpServers": {
    "limena": {
      "command": "<REPO>/.venv/bin/python",
      "args": ["-m", "app.ai.mcp.server"],
      "cwd": "<REPO>",
      "env": {
        "LIMENA_API_KEY": "<YOUR_API_KEY>"
      }
    }
  }
}

Authentication & keys

API keys
format
lmn_ + base58, shown once at creationThe secret is hashed at rest and never displayed again.
header
Authorization: Bearer lmn_…
create
Settings → API keysAvailable on Pro and up. Admin-scoped keys are restricted to the owner.
revoke
any key, any time; the next call with it is refused

Two-tier scopes

  • mcp:access is the connection scope. A key without it can open no MCP session at all.
  • Granular <family>:<read|write> scopes are checked per call. create_task needs tasks:write; search_contacts needs contacts:read. Holding mcp:access alone grants nothing beyond the handshake.
  • Grant the minimum set. A key carries exactly the scopes you select when you create it, so an agent can do only what its key allows.

Scopes

The scopes the published catalogue uses, grouped by resource family. Each is a read or write grant on one family, plus the mcp:access connection scope.

31 scopes
mcp:access
open an MCP connectionRequired to connect. Grants no data access on its own.
Contacts
contacts:read · contacts:write
Companies
companies:read · companies:write
Deals
deals:read · deals:write
Tasks
tasks:read · tasks:write
Notes
notes:read · notes:write
Meetings
meetings:read · meetings:write
Calls
calls:read
Outreach
outreach:read · outreach:write
Campaigns
campaigns:read · campaigns:write
Attachments
attachments:read
Search
search:read
Analytics
analytics:read
Users
users:read
bulk
bulk:write
imports
imports:read
lists
lists:read · lists:write
segments
segments:read · segments:write
tags
tags:read
templates
templates:read · templates:write

Capability reference

Every capability an MCP client can call, grouped by resource family. Mutating calls are flagged and need a write scope.

103 capabilities · 52 mutating

Contacts

contacts:read · contacts:write
  • archive_contactmutates

    Archive (soft-delete) a contact. It moves to Trash and can be restored later, not permanently erased. In the assistant this returns a confirmation card and the contact is archived only when the user confirms; over the API it is archived immediately.

  • bulk_create_contactsmutates

    Create up to 100 contacts atomically. Each entry may carry optional `tags` and `custom_fields` for per-contact enrichment (e.g. prospect_intel shapes), and an optional `owner_id` naming the teammate who will own that contact (omit to own it yourself). Any failure mid-batch rolls back the whole transaction: zero contacts are written. Returns {contacts: [<detail>, ...], created_count: N}.

  • bulk_find_emailsmutates10/hour

    Find work emails for up to 10 people in one call and add each hit as a new reachable contact, with per-person outcomes and the same provenance as find_email (observed vs inferred origin, source URLs, confidence, verification status). People are resolved one by one and the batch is not atomic: contacts already created stay created if a later lookup stops the batch. Per-person status is one of created, not_found, error, or skipped; when the monthly email finder quota runs out or the provider fails mid-batch, the batch stops and stopped_early names the reason with every remaining person reported as skipped, never dropped. Needs email finding enabled for the workspace.

  • correct_contact_emailmutates

    Correct a contact's email address, with provenance. update_contact deliberately cannot change email (an identity-stable patch); this is the audited path for fixing a wrong address. Requires provenance_url naming where the corrected address was observed; it is recorded in the audit trail beside the previous value. Returns a typed conflict when another active contact already has the address. Resets the bounce flag set against the old address and re-derives suppression for the new one. Stamps the email provenance custom fields (email_source correction, email_status unverified). Returns {contact, previous_email}.

  • create_contactmutates

    Create a new contact in the tenant's CRM.

  • describe_crm_schema

    The real schema of the CRM records you can read and write: per-entity columns with their type, nullability, and foreign keys, plus this tenant's custom fields (on contact/company/deal). Call it to answer what fields/columns an entity has. Report what it returns, never infer the schema from a tool's arguments. Pass an entity to scope it, or omit for all.

  • find_emailmutates10/hour

    Find a person's work email from their name + company (domain or name) and add them as a new reachable contact, with the email's provenance recorded: whether the address was observed on real web pages (with source URLs) or inferred from a pattern, plus confidence and verification status. Searches the open web first at no cost; a paid search runs only on a web miss and counts toward the workspace's monthly email finder quota. Confirms before the lookup + create. If no email is found, nothing is created; QUOTA_EXHAUSTED means the monthly allowance is used up and FINDER_UNAVAILABLE means the provider failed, so not_found always means a real miss. Needs email finding enabled for the workspace. Returns the resolved email + the created contact, or that none was found.

  • find_or_create_contactmutates

    Email-keyed find-or-create. Looks up the tenant's contacts by lowercased email; returns the existing record if matched, otherwise creates with the supplied fields. `first_name` and `last_name` are required only on the create path. `tags` and `custom_fields` apply only on the create path; the find branch returns the existing record unchanged, and any provided field that differs from the matched record is named in the response's warnings so the drop is never silent. Returns {contact, created: bool}.

  • find_similar_contacts

    Find contacts semantically similar to a seed contact (by contact_id) or to a free-text description (by query). Exactly one must be given. Returns contacts ranked nearest-first as {contact, distance} (cosine distance, 0 = identical). An unindexed seed returns an empty list.

  • get_contact

    Get full details for a specific contact by id.

  • restore_contactmutates

    Restore a soft-deleted contact from Trash back to active (the inverse of archive_contact). In the assistant this returns a confirmation card and the contact is restored only when the user confirms; over the API it is restored immediately. Use this to undo an archive. Refuses if the contact isn't in the trash.

  • search_contacts

    Search contacts by name, email, or company, or omit query to list them. Results are ordered by sort_by, newest first, so no query with the default sort returns the most recently added contacts. Can filter to the current user's own contacts (mine), one teammate's (owner_id), unassigned contacts with no owner (unowned), a tag, a status, one company (company_id), whether the contact has a dialable phone number (has_phone), or untouched leads. Returns the matched rows plus the total matched count; when total exceeds the page, fetch the rest with page=2, 3, …

  • update_contactmutates

    Partially update a contact's editable fields.

Companies

companies:read · companies:write
  • archive_companymutates

    Archive (soft-delete) a company. It moves to Trash and can be restored later, not permanently erased. Its contacts are unlinked (not deleted). In the assistant this returns a confirmation card and the company is archived only when the user confirms; over the API it is archived immediately.

  • create_companymutates

    Create a new company in the tenant's CRM. Mirrors the REST POST /companies endpoint.

  • find_or_create_companymutates

    Find-or-create on the canonical match key: normalised domain first, then case-insensitive name among active companies. Returns the existing record if either key matches (a name-match backfills a missing domain), otherwise creates with the supplied fields. tags, custom_fields, source and the other extras apply only on the create path: the find branch returns the existing record as-is without mutating it (updating a match is a separate update_company call), and any provided field that differs from the matched record is named in the response's warnings so the drop is never silent. `name` is required only on the create path. Returns {company, created: bool}.

  • find_similar_companies

    Find companies semantically similar to a seed company (by company_id) or to a free-text description (by query). Exactly one must be given. Returns companies ranked nearest-first as {company, distance} (cosine distance, 0 = identical). An unindexed seed returns an empty list.

  • get_company

    Get full details for a company by id, including contacts.

  • list_company_contacts

    List contacts that belong to a specific company.

  • restore_companymutates

    Restore a soft-deleted company from Trash back to active (the inverse of archive_company). In the assistant this returns a confirmation card and the company is restored only when the user confirms; over the API it is restored immediately. Contacts that were unlinked at delete time are NOT re-linked. Refuses if the company isn't in the trash.

  • search_companies

    Search companies by name or domain. Returns {companies, total} with contact counts, owner and location; when total exceeds the page, fetch the rest with page=2, 3, … Can filter to the current user's own companies (mine), one teammate's (owner_id), unassigned companies with no owner (unowned), companies carrying a tag (tag), a city, a region or nation, a country, a research source, a prospect-intel fit_rating or a prospect_stage. Filter here rather than paging and sorting on your side: geography and fit are the two things that used to require reading every company one at a time. For Scotland or England use region, not country: both nations store country=United Kingdom.

  • update_companymutates

    Update a company's fields. Pass only the fields to change. Domain input is canonicalised (scheme/path stripped).

Deals

deals:read · deals:write
  • archive_dealmutates

    Archive (soft-delete) a deal. It moves to Trash and can be restored later, not permanently erased. In the assistant this returns a confirmation card and the deal is archived only when the user confirms; over the API it is archived immediately.

  • create_dealmutates

    Create a new deal in the pipeline. Requires a contact; company is optional. Value is in cents. Stage defaults to the tenant's first open pipeline stage if omitted.

  • find_similar_deals

    Find deals semantically similar to a seed deal (by deal_id) or to a free-text description (by query). Exactly one must be given. Returns deals ranked nearest-first as {deal, distance} (cosine distance, 0 = identical). An unindexed seed returns an empty list. Use for 'find deals like our best closed ones' or 'deals about platform migrations'; for structured filtering by stage/owner use search_deals.

  • get_deal

    Get full details for a specific deal by id.

  • get_pipeline_summary

    Current deal pipeline grouped by stage with totals.

  • restore_dealmutates

    Restore a soft-deleted deal from Trash back to active (the inverse of archive_deal). In the assistant this returns a confirmation card and the deal is restored only when the user confirms; over the API it is restored immediately. Use this to undo an archive. Refuses if the deal isn't in the trash.

  • search_deals

    Search and filter deals. Supports filtering by stage, owner, contact, or company; use mine for the current user's own deals. Optional text query matches deal title. Returns {deals, total}; when total exceeds the page, fetch the rest with page=2, 3, …

  • update_dealmutates

    Update a deal's fields. Pass only the fields to change. Stage changes are audited with direction (forward/backward).

Tasks

tasks:read · tasks:write
  • archive_taskmutates

    Archive (soft-delete) a task. It moves to Trash and can be restored later with restore_task, not permanently erased. In the assistant this returns a confirmation card and the task is archived only when the user confirms; over the API it is archived immediately.

  • complete_taskmutates

    Mark a task as completed. Idempotent if already completed.

  • create_taskmutates

    Create a follow-up task. Optionally attach to a contact, deal, or company. Assignee defaults to the current user.

  • get_my_tasks

    Get open tasks assigned to the current user, sorted by due date (overdue first, then upcoming, then no-date). Use this whenever the user asks about their own tasks.

  • list_tasks

    List tasks, optionally filtered by entity or assignee. Returns open tasks by default; set include_completed=true for all.

  • restore_taskmutates

    Restore a soft-deleted task from Trash back to active (the inverse of archive_task). Find the task_id with search_trash, which lists archived records. In the assistant this returns a confirmation card and the task is restored only when the user confirms; over the API it is restored immediately. Refuses if the task isn't in the trash.

  • update_taskmutates

    Edit an existing task: reschedule (due_at; null clears the due date), set status (todo/in_progress/waiting/done), priority, size (s/m/l), reassign, rename, edit the description, or re-link it to a contact, deal, or company. Only the fields provided change; setting status to 'done' completes the task and leaving 'done' reopens it.

Notes

notes:read · notes:write
  • create_notemutates

    Add a note to a contact, deal, or company, or a comment to a task. To @-mention a teammate so they get notified, write @[Their Name](user:<user_id>) in the body. User ids come from list_users.

  • list_entity_notes

    List notes attached to a contact, deal, company, or task (a task's notes are its comments). Returns newest first.

  • recent_notes

    List notes created across ALL records in the last N days, newest first, optionally filtered to notes whose body contains a given text. Use for 'any notes about pricing this week?' or 'what did I note recently?' when you don't have a specific record in hand. For notes on ONE known record, use list_entity_notes instead.

  • search_notes

    Semantic search across ALL note bodies: matches by meaning, not substring, so 'pricing pushback' finds a note that says 'thought the quote was steep'. Returns notes ranked nearest-first with their parent record reference and a cosine distance (0 = identical). Use when the user half-remembers a note; for literal text or a time window use recent_notes, and for one known record use list_entity_notes.

  • update_notemutates

    Edit an existing note or task-comment: replace its body with new text (pass the whole body, not a diff). The note keeps its author and parent record. Get the note_id from list_entity_notes, recent_notes, or search_notes. Only the note's author or an admin may edit it.

Meetings

meetings:read · meetings:write
  • list_meetings

    List meetings logged against a contact, company, or deal. Returns newest first.

  • log_meetingmutates

    Log a meeting against a contact, company, or deal: date, duration, attendees, location, and notes. Records a past or scheduled meeting; surfaces on the entity's timeline.

  • recent_meetings

    List meetings logged across ALL records in the last N days, newest first, optionally narrowed to one entity kind. Use for 'what meetings happened this week?' when you don't have a specific record in hand. For meetings on ONE known record, use list_meetings instead. Distinct from list_upcoming_events, which reads the user's real connected calendar.

  • update_meetingmutates

    Edit a logged meeting's details: title, when it happened, duration, location, or notes. Pass only the fields to change. Get the meeting_id from list_meetings or recent_meetings. This edits a logged meeting record; for a calendar invite use update_calendar_event instead.

Calls

calls:read
  • list_call_recordings

    List captured calls (uploaded recordings with an AI transcript and summary), newest first. Calls are logged against a contact, company, or deal, or are the team's internal calls (standups, deal reviews) via entity_type 'internal' with no entity_id. Returns each call's title, date, transcript/summary status, AI summary, sentiment, action items, and suggested next step. Use to recall what was discussed on recent calls with a record or in team meetings.

Outreach

outreach:read · outreach:write
  • draft_email

    Generate a {subject, body} email draft from a brief, using the tenant's configured LLM. Read-only: no DB write. The caller passes the returned draft to send_one_off_email (or discards it). Optional reply_to_outreach_id enriches the prompt with the inbound thread for reply drafting.

  • get_campaign_summary

    Aggregate outreach stats for a date range or campaign by name. Returns total_sent, total_replied, reply_rate, open_rate, bounce_rate, by_channel.

  • get_outreach_history

    Get the most recent outreach records for a contact.

  • get_recent_outreach

    Most recent outreach records, optionally filtered to one contact. Useful for context before drafting a follow-up.

  • log_outreachmutates

    Record an outreach event against a contact.

  • send_one_off_emailmutates

    Stage a single-recipient email send through the human-approval queue (M33d). Creates a pending SendApprovalRequest backed by an implicit one-off campaign; a human must approve in the Limena UI before the email leaves. Idempotent within 5 minutes per tenant+contact. When reply_to_outreach_id is supplied (M33f), the send is threaded as a reply to that inbound message: In-Reply-To and References headers are attached to the outgoing email and, for Gmail, the message lands in the same conversation.

  • upload_outreach_feedbackmutates

    Bulk-record outreach status updates from an external mailmerge tool. Unmatched emails create new contacts; the response lists them.

Campaigns

campaigns:read · campaigns:write
  • add_contacts_to_campaignmutates

    Add individual contacts to a campaign as manual-source recipients. Up to 500 contacts per call. Idempotent: contacts already in the campaign via this source are silently skipped. Returns suppression breakdown (unsubscribed, email_invalid).

  • add_list_to_campaignmutates

    Add all contacts from a prospect list to a campaign. Auto-promotes eligible list items to contacts. Returns suppression breakdown and promotion count.

  • add_segment_to_campaignmutates

    Add contacts matching a saved segment to a campaign. Snapshot at add-time. Re-add the segment to pick up new matches. Returns suppression breakdown.

  • cancel_campaign_sendmutates

    Cancel a campaign send that is sending, paused, or scheduled. Already-sent messages are not retracted, so cancelling a send that has started cannot be undone. Cancelling a scheduled send that has not fired yet only removes the schedule: an AI campaign returns to its review queue with its drafts intact and can be scheduled again.

  • check_send_approval

    Check the status of a send approval request. Returns pending, approved, or rejected with decision details, plus recipients_blocked_unverified (addresses that were pattern-derived and never checked, already excluded from the send) and recipients_unconfirmed (addresses that WILL be sent to but carry no record of where they came from). Read both before approving a send.

  • create_campaignmutates

    Create a new campaign. Idempotent: returns the existing campaign if one with the same name already exists.

  • get_campaign

    Get campaign detail including member count, outreach count, send status, and last sent date.

  • list_campaign_members

    List contacts that are members of a campaign.

  • pause_campaign_sendmutates

    Pause a campaign that is currently sending. Already-queued messages will self-skip. Resume must be done by a human via the UI.

  • remove_contact_from_campaignmutates

    Remove a contact from a campaign across all sources. Idempotent: returns removed=false if the contact was not a member.

  • request_campaign_sendmutates

    Request approval to send a campaign. Creates a pending approval that a human must approve or reject in the Limena UI before any emails are sent. Returns the approval ID for polling via check_send_approval. Idempotent: a second call while one is pending returns the existing approval.

  • update_campaignmutates

    Update campaign metadata. Only send the fields you want to change. Supports: name, description, compose_mode (template, ai_generated, or ai_templated), tracking_enabled, system_prompt_override.

Attachments

attachments:read
  • list_entity_attachments

    List file attachments on a contact, deal, company, task, or note. Returns newest first. Read-only: uploads + deletes go through the REST API.

Search

search:read
  • search_crm

    Hybrid keyword + semantic search across the CRM. Exact name / email / title matches rank first; contacts and companies also surface semantically related records a keyword miss would skip. Returns up to per_type results in each bucket: contacts, companies, campaigns, deals, outreach.

  • search_everything

    ONE semantic search across the whole workspace (contacts, companies, deals, notes, calls, email threads, meetings, tasks, form submissions and document attachments), ranked nearest-first by meaning with a relevance cutoff. Use FIRST for open 'what do we know about X' questions instead of fanning out across per-type search tools; each hit is a compact {entity_type, id, label, snippet, date, distance} line; follow up with the type's detail read for anything relevant. For structured filtering (stage, status, owner lists) use the per-type search tools; for exact-name lookup use search_crm.

  • search_trash

    Search the Trash (soft-deleted, archived contacts, companies, deals and tasks) by name, title or subject. The regular search tools never return archived records; this one returns ONLY them, each as a {entity_type, id, label, archived_at} line. Use it to find a record to bring back that was archived in another conversation, then call restore_contact / restore_company / restore_deal / restore_task with the id. Omit the query to list everything currently in the trash.

Analytics

analytics:read
  • campaign_comparison

    Compare reply rates or open rates across named campaigns.

  • count_records

    Total record count for a tenant-scoped entity, with the same typed filters as the search tools so 'how many companies are unassigned' is one call.

  • list_campaigns

    List all campaigns for the tenant with member and outreach counts. Use to discover campaign names before filtering by them.

  • outreach_by_channel

    Breakdown of outreach volume and reply rates by channel.

  • outreach_summary

    Aggregate outreach stats for a date range / campaign.

  • query_analytics10/hour

    Natural language analytics question. Delegates to the AnalyticsAgent; consumes LLM tokens; rate-limited 10/hour.

  • recent_activity

    Most recent outreach and deal stage changes.

  • top_contacts

    List contacts ranked by engagement metric. Each result carries its contact_id, usable directly in follow-up calls (add to campaign, update, get) without re-searching by name.

Users

users:read
  • list_users

    List the tenant's active team members. Useful for discovering user IDs when assigning tasks or deal ownership. Deactivated accounts are never listed, because they cannot be assigned work.

bulk

bulk:write
  • bulk_reassign_companiesmutates

    Reassign a set of companies (by id, 1 to 100) to a single owner in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. Omit owner_id to assign them to yourself; pass a teammate's owner_id (resolve it with list_users) to assign to them; set unassign=true to clear the owner. Resolve the set first with search_companies (e.g. unowned=true) so you act on the exact companies. Returns {matched, affected, skipped}.

  • bulk_reassign_contactsmutates

    Reassign a set of contacts (by id, 1 to 100) to a single owner in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. Omit owner_id to assign them to yourself; pass a teammate's owner_id (resolve it with list_users) to assign to them; set unassign=true to clear the owner. Resolve the set first with search_contacts (e.g. unowned=true) so you act on the exact contacts. Returns {matched, affected, skipped}.

  • bulk_reassign_dealsmutates

    Reassign a set of deals (by id, 1 to 100) to a single owner in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. Omit owner_id to assign them to yourself; pass a teammate's owner_id (resolve it with list_users) to assign to them. A deal always has an owner, so it cannot be unassigned. Resolve the set first with search_deals so you act on the exact deals. Returns {matched, affected, skipped}.

  • bulk_set_status_contactsmutates

    Set the lifecycle status on a set of contacts (by id, 1 to 100) in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. The status must be one of the tenant's configured statuses (setting 'unsubscribed' also suppresses future outreach). Resolve the set first with search_contacts so you act on the exact contacts. Returns {matched, affected, skipped}.

  • bulk_tag_companiesmutates

    Add or remove tags on a set of companies (by id, 1 to 100) in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. mode='add' adds every tag to every company (idempotent); mode='remove' removes them. Resolve the set first with search_companies so you act on the exact companies. Returns {matched, affected, skipped}.

  • bulk_tag_contactsmutates

    Add or remove tags on a set of contacts (by id, 1 to 100) in one step. In the assistant the whole set is shown as one confirmation card before anything changes; over the API it applies immediately, so pass dry_run=true first to see the exact set. mode='add' adds every tag to every contact (idempotent); mode='remove' removes them. Resolve the set first with search_contacts so you act on the exact contacts. Returns {matched, affected, skipped}.

imports

imports:read
  • get_import_job

    Get one CSV import job by id with its status, imported/duplicate/error counts, failure reason, and whether an error report is available. Use to explain how an import went.

  • get_migration_job

    Get one CRM migration job by id with its overall status, per-file breakdown (each file's entity type, status, row + error counts), the unresolved-foreign-key count, and any failure reason. Use to explain how a migration went, file by file.

  • list_import_jobs

    List the tenant's single-file CSV import jobs, newest first, each with its status, row counts, and any failure reason. Use to answer 'did my import finish' or to find a job to inspect.

  • list_migration_jobs

    List the tenant's multi-file CRM migration jobs (e.g. a HubSpot export), newest first, each with its vendor, status, file count, and error count. Use to answer 'did my migration finish'.

lists

lists:read · lists:write
  • create_prospect_listmutates

    Stage a set of researched prospects as a new prospect list, so the existing list pipeline (dedupe, cross-reference against the user's contacts, disposition) takes over. Use this to save the results of a web research request. Columns are kept as given. In the assistant the user confirms before the list is created, so say it was proposed and is awaiting confirmation; over the API it is created immediately.

  • get_prospect_list

    Get one prospect list by id with its per-disposition cleaning breakdown (safe-to-email, duplicate, previously-contacted, invalid email) and the known-contact tally. Use to report how a list cleaned, or to check it before promoting its safe rows to contacts.

  • list_prospect_lists

    List the tenant's prospect lists (uploaded or AI-researched), newest first, with each list's cleaning progress and safe-to-email count. Use to answer 'what lists do I have' or to find a list before reading or promoting it.

  • promote_prospect_listmutates

    Promote a prospect list's safe-to-email rows to CRM contacts (creating companies as needed). Already-known people are linked, not duplicated. Reports how many were created, already existed, or were skipped. In the assistant the user confirms before any contact is created; over the API it runs immediately, so check the list with get_prospect_list first.

segments

segments:read · segments:write
  • create_segmentmutates

    Save a named, reusable contact segment from a structured SegmentQuery filter. Deterministic: the query is compile-checked against the field registry and stored as given, with nothing generated. Fails cleanly if the name is already taken. Use list_segments to see what exists first.

  • delete_segmentmutates

    Permanently delete a saved segment (the named filter only. No contacts are touched, and campaigns already built from it keep their recipients). There is no trash for segments, so this cannot be undone. Get the segment_id from list_segments.

  • list_segments

    List the tenant's saved segments (named, reusable filters over contacts, e.g. 'UK prospects, no email in 30 days'), newest first. Use to find a segment to report on or to target with a campaign.

  • update_segmentmutates

    Rename a saved segment and/or replace its filter query (the query replaces wholesale, not a merge: send the complete new SegmentQuery). At least one of name or query is required. Get the segment_id from list_segments.

tags

tags:read
  • list_tags

    List the workspace's whole tag vocabulary, alphabetically, with the number of records carrying each tag and the slug it is matched on. Read this BEFORE writing tags on a contact or company: a tag name is matched by its slug (lowercased, every run of non-alphanumeric characters becomes a hyphen), so 'M&A' and 'M and A' are different tags, and writing a name that slugs differently from an existing tag silently creates a near-duplicate rather than reusing it.

  • search_tags

    Find existing tags matching a piece of text, ranked exact-slug then prefix then substring, most-used first. Use it to check whether a tag you are about to write already exists under a different spelling before you create a second one.

templates

templates:read · templates:write
  • create_email_templatemutates

    Save a reusable email/campaign template from an explicit subject and body. Deterministic: the text is stored as given and nothing is generated. Fails cleanly if the name is already taken (use list_email_templates to check). Merge variables are {{double_braced}} and the contact ones are bare, not namespaced: {{first_name}}, {{last_name}}, {{email}}, {{phone}}, {{job_title}}, {{linkedin_url}}, plus {{company.name}} (the only dotted one) and the tenant's own {{custom:<key>}} fields. Anything else, such as {{contact.first_name}} or {{company_name}} or {{company.domain}}, is not a variable and sends as an empty string, so the result lists any it does not recognise under `warnings`. Do not add a {{signature}} or a written sign-off: the sender's signature is appended automatically at send time.

  • get_email_template

    Get one email template by id with its full subject, body, and merge variables. Use to read or reuse a template's content when drafting an email.

  • list_email_templates

    List the tenant's saved email/campaign templates, most-recently-updated first, optionally filtered by folder. Returns each template's name, subject, merge variables, and folder. Use to find a template to reuse or reference.

  • update_email_templatemutates

    Edit a saved email template: rename it, replace its subject or body (each replaces wholesale, not a diff), or refile it. At least one change is required. Get the template_id from list_email_templates. Merge variables are {{double_braced}} and the contact ones are bare, not namespaced: {{first_name}}, {{last_name}}, {{email}}, {{phone}}, {{job_title}}, {{linkedin_url}}, plus {{company.name}} (the only dotted one) and the tenant's own {{custom:<key>}} fields. Anything else, such as {{contact.first_name}} or {{company_name}} or {{company.domain}}, is not a variable and sends as an empty string, so the result lists any it does not recognise under `warnings`. Do not add a {{signature}} or a written sign-off: the sender's signature is appended automatically at send time.

Governance

Governance is a property of the surface, not the agent’s goodwill. Every MCP call passes the same checks, in code.

What governs an MCP call
authentication
scoped Bearer API keysmcp:access to connect; a granular scope checked on every call.
rate_limits
120 requests / minute per keyquery_analytics is limited to 10 / hour, since it runs an LLM.
audit
every mutation is written to the audit logRecorded with the acting key. Mutations write two rows (the service event and an mcp_client provenance row); reads are not logged.
isolation
PostgreSQL row-level security, per tenantThe app role has no BYPASSRLS; a query without a tenant context returns zero rows.

MCP calls execute immediately. There is no confirm step on this surface.

A mutating call runs the moment your agent invokes it, governed by the key’s scopes, the rate zone, and the audit log. The proposal-and-confirm gate, where a person approves every write before it runs, belongs to Limena’s in-app assistant, a separate surface. Over MCP, a key acts on its own authority, so grant scopes deliberately.

REST API

Beyond MCP, the core resources are a REST API documented with OpenAPI. Same lmn_ Bearer keys and the same granular scopes.

  • Swagger UI is the interactive reference. ReDoc and the raw OpenAPI document are published too.
  • The MCP endpoints are a streaming mount and are not part of the OpenAPI document; use the capability reference above for those.

Build something on your CRM.

Want it wired into your stack? We’ll walk the surface with you on your own data.

{
  "method": "tools/call",
  "params": {
    "name": "create_task",
    "arguments": {
      "subject": "Follow up with Helix Logistics",
      "due_at": "2026-06-19",
      "entity_type": "deal"
    }
  }
}