• Virtual Marketer CRM logo
    Virtual Marketer CRMDocumentation
  • Introduction
Getting Started
  • Quickstart
  • Core Concepts
Connect your AI
  • Custom connector
  • CLI & editors
  • Rate limits
Integrations
  • MCP
  • Webhooks
  • Website leads
  • OpenAPI 3.1.0
  • N8N
  • WhatsApp API
App guide
  • Dashboard
  • Inbox
  • Records
  • Profile
  • Company
  • Lead sending
  • API Keys
  • Filter Syntax
Licensing & security
  • Architecture & Security
  • Go back
  1. Introduction
  2. MCP

Model Context Protocol (MCP)

Virtual Marketer CRM exposes a native MCP endpoint at /api/v1/mcp so Claude, ChatGPT, Cursor, and other agent clients can read and write your CRM directly.

Virtual Marketer CRM exposes one MCP endpoint at https://crm.virtual-marketer.de/api/v1/mcp. A connected client discovers all 62 CRM tools automatically. There are two ways to connect:

  • Custom connector (OAuth): for Claude (web, desktop, mobile) and ChatGPT. Paste the URL, sign in, approve. No key to manage. Start on Connect with a custom connector.
  • API key: for CLI and editor clients (Claude Code, Codex, Cursor, Gemini CLI) and raw HTTP. Send the 64-character key in the x-api-key header or a config file.

For end-to-end setup on one page, jump to your client: Claude Desktop, ChatGPT, Claude Code, Codex, Cursor, or Gemini. This page is the protocol reference.

When to use MCP

  • You want your AI to read and write the CRM without copying IDs around.
  • You want the AI to discover capabilities rather than hand-writing API calls.
  • You want one endpoint that works across Claude, ChatGPT, Cursor, Codex, and other clients.

Use OpenAPI instead when an engineer or integration service already knows which endpoint it needs. OpenAPI is the canonical HTTP reference. MCP is the agent-native interface built on top.

The endpoint

POST https://crm.virtual-marketer.de/api/v1/mcp
Content-Type: application/json
Accept: application/json, text/event-stream
x-api-key: <your-64-character-key>

The endpoint speaks the Model Context Protocol (streamable HTTP variant). tools/list returns every tool with its JSON Schema. tools/call invokes a tool by name.

Because it is the streamable HTTP variant, every request must send Accept: application/json, text/event-stream. Without it the endpoint returns 406 Not Acceptable. All supported MCP clients set this automatically; only raw HTTP callers (curl, scripts) need to add it explicitly.

The x-api-key header is the API-key method. The key is 64 characters, base62 (a-z, A-Z, 0-9), and inherits the permissions of the user who created it. There is no per-key scoping. Custom-connector clients (Claude, ChatGPT) authenticate over OAuth instead and send a bearer token they obtain and refresh for you. See Connect with a custom connector.

Connect a client

ClientMethodGuide
Claude web & mobileCustom connector (OAuth)Connect with a custom connector
Claude DesktopConnector (OAuth) or config keyConnect Claude Desktop
ChatGPTConnector (OAuth) or key headerConnect ChatGPT
Claude CodeAPI keyConnect Claude Code
CodexAPI keyConnect Codex
CursorAPI keyConnect Cursor
Gemini CLIAPI keyConnect Gemini
Any MCP clientKey headerUse the endpoint and header above

The server briefs the model automatically the moment a client connects. Clients that support MCP prompts can also run the built-in get-started prompt for a personalized start.

Server instructions, prompts, and toolsets

You do not need to teach the model how to use the CRM. The server sends instructions when a client connects, so the model follows the workflow automatically: read the schema first, find ids before writing, change relations only through manage_record_links, and confirm before deleting or sending. In clients that support MCP prompts (Claude Code, Cursor), a built-in get-started prompt is available too; it interviews you and summarizes your workspace to personalize the start.

The full 62-tool surface is the default. Append ?toolsets= to the endpoint URL to narrow it, for example /api/v1/mcp?toolsets=records,messaging. Keys and details are in Narrowing with ?toolsets=.

The Virtual Marketer Agent

The server introduces itself to every client as the Virtual Marketer Agent (serverInfo.name in the MCP handshake), and its instructions ask the client to refer to it by that name. It is the agent and data layer of the Virtual Marketer platform: it holds the workspace data, enforces who may see and change what, and keeps what it learns inside the workspace.

What the privacy promise rests on, and nothing more:

  • Every call runs as one user or one service identity and sees only that workspace. A service key reaches exactly the permissions of its role.
  • The CRM holds no key of an AI provider, so no record leaves the server for training or inference through the CRM itself. What a connected client does with the data it reads is that client's responsibility.
  • Error texts never carry hostnames, ports or database messages.
  • Every write through the application lands in the audit log with author and time.

Explicit rules and implicit learning

A field can be steered in two ways, and the two work together:

  • Explicitly: a custom column can carry a description. It is shown in the column editor and to every agent in get_record_schema. Write the rule there, for example how long a subject may be. An explicit rule always wins.
  • Implicitly: a custom column marked learnFromEdits turns every edit a person makes into a learning signal. get_learning_signals lists those edits with the value before and after and who wrote the value before (previousBy: agent, person or unknown). A person correcting an agent draft is the strongest signal. Edited notes sections, such as an Instagram DM, are reported per section.

The learning run reads these signals, adapts tone and wording of later drafts, and watches for drift. It never turns a learned pattern into a change of target group or offer on its own: such a change is proposed to a person.

Critical review of target group and style

A custom column marked reviewOnChange is consequential, typically the target group or a style field. An update_* call that changes it is refused and writes nothing unless it carries a reviewNote: what changes, why it fits the workspace briefing and the learning signals, and who asked for it. The note is stored with the audit entry and shows up in get_audit_changes and get_learning_signals. The same review applies by instruction to a workspace briefing record: an agent reads it before it changes it and raises contradictions instead of writing them.

Set all three per column in the column editor or with manage_custom_columns (description, learnFromEdits, reviewOnChange).

How the tool surface is shaped

The MCP surface is built so that models with weaker planning can use it reliably:

  • Verb-first imperative names: create_contacts, update_deals, delete_records. No batch prefix. The verb matches intent.
  • Merged periphery: custom columns, widgets, and webhooks each live behind one tool (manage_custom_columns, manage_widgets, manage_webhooks) with an action switch, so the model picks an action instead of choosing among many near-identical tools.
  • Inline enum hints: every enum field lists its valid values inline in the description, so the model does not have to resolve external types.
  • Filter examples inline: every filters parameter has a concrete JSON example in its description.
  • Relationship safety: relations change only through manage_record_links (add or remove); passing null on a relationship array is rejected with a remediation hint.
  • Immutable column type: manage_custom_columns keeps type and entityType fixed on update (taken from the existing column); changing a type means delete and recreate.
  • Destructive flags: every destructive tool or action has destructiveHint: true and an IRREVERSIBLE description prefix.

CLI use

If you prefer a local client over a GUI, tools like mcporter can connect to the same endpoint. Store the API key once in the client config and call tools from the shell.

OpenAPI alongside MCP

Both live at the same base URL. MCP is /api/v1/mcp; the OpenAPI spec is at /api/v1/openapi. The OpenAPI operations map 1:1 to REST endpoints; MCP tools wrap those and add safety guardrails the raw API does not have.

Tool catalog

Virtual Marketer CRM exposes 62 MCP tools, all enabled by default. They cover records, workspace, messaging, documentation and deep research, custom columns, widgets, webhooks, projects, campaigns, insights, teams, billing, time tracking, enrichment, WhatsApp, admin, support and audit. Every destructive tool is flagged and starts its description with IRREVERSIBLE. Relations change only through manage_record_links; the update tools never touch them.

Two flags per tool:

  • Read: no mutation.
  • Destructive: deletes data or cannot be undone. For merged tools the flag applies to their delete-capable actions.

Full JSON Schema for every tool is available live at POST /api/v1/mcp with method: "tools/list".

Records

Seventeen tools work against the five record types (contact, organization, deal, service, task). Records carry custom columns defined per workspace, so get_record_schema is the anchor: it returns the custom-column ids and option values that writes need. Fields such as a deal or task status are configurable singleSelect custom columns, not fixed native fields; get_record_schema returns whatever columns the workspace actually has.

ToolReadDestructivePurpose
get_record_schema✓Schema and custom-column metadata, never record data. One entity type, or all five when entity is omitted. Call before any create or update. An organization date column that fills itself carries autoFilledFrom with the id of the column that triggers it.
list_records✓Search, filter, sort, paginate one entity type. Always returns the total. Deals include totalValue and totalQuantity, services include amount.
search_records✓Free-text search across one or more entity types in one call.
get_records✓Full record data by id, up to 100, mixed entity types allowed; contacts also by email, phone, or provider:handle. Always returns fields; add markdown notes per item with include=withNotes.
create_contactsCreate up to 100 contacts, custom-column values and relation ids inline.
create_organizationsCreate up to 100 organizations, custom-column values and contact/user/deal/task ids inline.
create_dealsCreate up to 100 deals, services as an inline array.
create_servicesCreate up to 100 services, custom-column values and user/deal/task ids inline.
create_tasksCreate up to 100 tasks, custom-column values and relation ids inline. Accepts projectId, parentTaskId and sortOrder to place the task in a project or under a parent task, and dueDate (ISO 8601 date or date-time) as its deadline.
update_contactsPartial update by contact key (id, email, phone, or provider:value); org/deal/user/task relations untouched, but a provided identifiers array REPLACES the contact's messaging channels (unlisted ones are unlinked).
update_organizationsPartial update by id. Never touches relations. A change to a column marked reviewOnChange needs a reviewNote, otherwise nothing is written; the same holds for the other update_* tools.
update_dealsPartial update by id, including singleSelect custom-column values and services (an inline {serviceId, quantity} array that REPLACES the deal's full service set); org/user/contact/task relations untouched (use manage_record_links).
update_servicesPartial update by id.
update_tasksPartial update by id, including singleSelect custom-column values, projectId, parentTaskId, sortOrder and dueDate (null clears the due date). Never touches relations.
update_record_notesReplace or append markdown notes on 1 to 100 records, selected by mode.
add_record_attachmentStore a file on an organization: audio plays inline, images, PDF; at most 10 MB as base64. Audio requires a caption, shown word for word next to the player.
list_record_attachments✓List an organization's files with caption and size, newest first.
get_lead_dispatches✓Shows the automatic lead sending: each rule (running or paused, reason, sending window, sender, counts per status) and the individual dispatches with recipient, subject, status, send time and failure or bounce reason. Check it before researching or writing to a lead to see whether that organization was already mailed, bounced or is still waiting. See Lead sending.
get_lead_follow_ups✓Shows the lead replies waiting for review: the lead's answer and the exact second mail (with the Media Kit link) that would go out. Read the reply before deciding with decide_lead_follow_up.
decide_lead_follow_up✓Approves or skips one held follow-up mail with a required reason. Send only when the reply shows interest; skip on any decline, auto-reply or unclear case. The text cannot be edited, and the CRM re-checks everything at send time.
get_learning_signals✓The implicit half of learning: edits people made to organization columns marked learnFromEdits and to notes sections, newest first, each with the value before and after, the editor, and previousBy (agent, person or unknown). Edits by service identities are left out. Page with before. Needs read access to organizations.
manage_record_linksAdd or remove ids on one relation (action add or remove). The only way to change relations.
delete_records✓IRREVERSIBLE hard-delete of 1 to 100 records by id (contacts also by email, phone, or provider:value).

All create and update tools take custom-column values via customFieldValues; call get_record_schema first for the column ids.

Workspace

ToolReadDestructivePurpose
get_workspace_context✓Your user, the company profile, all roles including permissions, and your connected messaging accounts (your own plus any shared with the workspace) in one call. The natural first call of a session.
list_users✓Team members with id, name, email, roleId, and status.

Messaging

The messaging tools are registered and documented here. The inbox behind them is being rebuilt as a native email and WhatsApp engine.

ToolReadDestructivePurpose
get_messaging_threads✓Two modes: without threadId lists inbox threads with filters and sorting (threads with no message yet are hidden unless they hold a draft); with threadId returns one thread plus a page of its messages (default 25, newest first, drafts included).
get_message_attachment✓Downloads one attachment of an inbox message: images and audio are returned as viewable content, other types as a description plus, with includeRawBase64, the raw bytes. Fetches from the provider on first call and caches.
get_activities✓Activity timeline (messages and audit-log changes) for the workspace or one record.
send_chat_messageDelivers immediately. With threadId replies in an existing chat; with connectedAccountId plus attendeeIdentifiers starts a new one (optional chatName names a group).
send_emailDelivers immediately. Send or reply from a connected email account; can send a saved draft via draftMessageId.
save_message_draftPrepare a reply for review: the draft shows up in the inbox compose box and the user sends it. Drafts are thread-bound, one per thread, saving again updates it.
discard_message_draft✓Delete a draft by its draft message id.
update_messaging_threadSet the thread state: unread, open, closed, or spam.
connect_messaging_accountGenerate a link the user opens in a browser to connect a channel (WhatsApp or email). You return the link; the user finishes auth there. Expires in 30 minutes.
pause_lead_sendingPauses the automatic lead sending, every rule of the workspace when ruleId is omitted. Queued mails stay queued, one already being handed over finishes. There is deliberately no resume over MCP: only a signed-in person in the CRM can resume.

Documentation and deep research

ToolReadDestructivePurpose
search_docs✓Full-text search over the docs; defaults to the product guides (source=docs); pass source=api or all to include the REST API reference. Returns slug, source, title, url, snippet.
get_docs_page✓One documentation page as markdown with its canonical URL. Lists valid slugs on a miss.
search✓Required by ChatGPT deep research connectors; federates CRM records and docs. Interactive agents should prefer search_records or search_docs.
fetch✓Deep-research companion to search: fetches one result by its id.

Custom columns

ToolReadDestructivePurpose
manage_custom_columns✓One tool with an action switch: list, upsert (create or update), delete, set_auto_date, remove_auto_date. Covers all ten column types; type and entityType are immutable on update. Delete is IRREVERSIBLE and removes every stored value. set_auto_date makes an organization date column (id) fill itself with today's date the first time another organization column (sourceColumnId) gets a value; remove_auto_date switches that off. Upsert also sets description (the explicit rule agents read), learnFromEdits (edits by people become learning signals) and reviewOnChange (changes over MCP need a reviewNote).

Widgets

ToolReadDestructivePurpose
manage_widgets✓One tool with an action switch: list, get, create, update, delete. The get action returns the computed data points alongside the configuration, so aggregate totals come straight from a widget.

Webhooks

ToolReadDestructivePurpose
manage_webhooks✓One tool with an action switch: list, get, create, update, delete, plus the delivery log (action list_deliveries, scoped to one webhook's current url when you pass its id) and re-delivery (action resend_delivery).

Projects

ToolReadDestructivePurpose
manage_projects✓One tool with an action switch: list, get, create, update, archive, delete. Members are workspace user ids and an update REPLACES the whole member list. startDate/endDate (YYYY-MM-DD, end not before start) set the time frame and progress (0 to 100) the completion; on update, omitted values are kept and null clears a date. Prefer action archive over delete: deleting a project keeps its tasks but makes them project-less.

Campaigns

ToolReadDestructivePurpose
get_campaigns✓Lists the e-mail and WhatsApp campaigns with status, sender, sending window, pacing and counts per recipient state. With id it returns one campaign plus a page of its recipients (address, consent basis, status, reason); senders: true lists the mailboxes an e-mail campaign may use.
manage_campaigns✓One tool with an action switch: create, update, add_recipients, remove_recipients, pause, cancel, delete. It prepares and stops campaigns but can never start or resume one: that is only possible for a person in the CRM interface. For e-mail, consentBasis is required and is a legal statement that must come from the user; express_consent needs consentEvidence, existing_customer is refused for contacts without a deal or invoice. Suppressed and unsubscribed addresses are skipped. A WhatsApp campaign can only be cancelled, not paused.

Insights and follow-ups

ToolReadDestructivePurpose
get_sales_insights✓The standard sales dashboard: net revenue per month for 12 months (paid and open), the last 12 months against the 12 before, recurring revenue per month, active, former and new customers, open and overdue invoices, open offers, the lead funnel, the distribution of every lead or status column and the open follow-ups per kind. Money is integer cents.
get_follow_ups✓Lists the detected follow-ups, highest priority first: unpaid invoices (1, 7 and 14 days after the due date), open offers, orders without invoice, stalled deals, leads without reply, former customers and upsell chances from what similar customers bought. Each carries its reason, the recommended channel and recipient, du or Sie from the mail history and a ready draft. settings: true returns the workspace settings.
update_follow_upaction set_status marks a follow-up done or dismissed and removes its task; action scan runs detection now. Neither sends anything: a message goes out only from the Insights page or a send tool, with the user's go.

Teams

ToolReadDestructivePurpose
manage_teams✓One tool with an action switch: list, get, create, update, delete, add_member, remove_member, list_rules, set_rule, delete_rule, resolve. A team's key is the stable lowercase handle a ticket router addresses it by. A routing rule with no organizationId is the workspace default; one with an organizationId overrides it for that single customer. Strategy all notifies every assignee, roundRobin picks one by rotationSeed, leastOpen picks whoever carries the fewest assigned tasks, none deliberately leaves the ticket unassigned. When no rule matches, the team leads answer, then the whole team. Action resolve answers "who would get this ticket" without writing anything.

Billing

ToolReadDestructivePurpose
manage_billing✓One tool with an action switch for the offer → order → invoice → credit note chain: list, get, create, update, status, convert, payment, send, einvoice, profile, delete. All money is integer CENTS (1250 means 12.50), and totals are always computed from the lines, never supplied. Leaving draft assigns the permanent number, which never changes afterwards; a numbered document can only be cancelled, not deleted. Action send attaches the PDF and, when the invoice is complete, the e-invoice; pass scheduledSendAt to queue it instead, or testCopy: true with an explicit to for a trial run that allocates no number, marks nothing as sent and never reaches the customer.
manage_recurring_invoices✓One tool with an action switch: list, create, update, delete. A series repeats an existing invoice as its template; the template itself is never sent. With autoSend every issue goes out immediately, otherwise it lands as a draft for review. status: paused stops a series without deleting it.
get_finance_report✓Two reports: datev returns a DATEV EXTF booking batch for the period plus the documents it had to skip and why, revenue-plan returns a month-by-month forecast from payments received, open invoices, confirmed orders, projected recurring invoices and open offers weighted by offerProbabilityPercent.

Time tracking

ToolReadDestructivePurpose
manage_time_tracking✓One tool with an action switch: list, create, update, delete, start, stop, running, preview_billable, bill, release. Durations are whole MINUTES, rates are integer CENTS per hour: 9000 means 90.00 per hour. An entry needs a taskId or an organizationId, and time booked on a task also counts for the customers that task is linked to. Omit the rate to fall back to the service rate and then to the workspace default. Action preview_billable shows the invoice lines an organization's unbilled time would produce without writing anything; action bill appends them to a DRAFT document and marks the entries invoiced in one transaction, so the same hour can never reach two invoices. A document that has left draft is refused. Action release takes the time back off a draft and removes the lines it added.

Enrichment

ToolReadDestructivePurpose
infer_contact_email✓Derive a likely email address from the pattern colleagues at the same domain already use. Returns a proposal with a confidence of strong or weak. Weak means one supporting address, or a German umlaut whose folding (mueller vs muller) the domain never demonstrated. Treat a weak proposal as a guess and say so; never send to an inferred address without telling the user it was inferred. Omit domain and pass only contactId to use the domain most colleagues at that contact's organisation already use.

WhatsApp

ToolReadDestructivePurpose
manage_whatsapp_session✓Pair or unpair the calling user's own WhatsApp number on the self-hosted WAHA instance. Each user gets their own session, never shared with colleagues. Action connect returns a QR code that a human has to scan on the phone owning the number; action status re-reads the pairing state and returns a fresh code; action logout unpairs. Connect and logout need administrator rights.
send_whatsapp_messageSend a text message, optionally with base64 file attachments (photo, voice note or document by MIME type, 25 MB in total), from the paired number. Only for conversations the contact started or consented to. WAHA is an unofficial transport, and cold outreach risks the number being banned and is unlawful under UWG §7 in Germany. A number connected through the official Cloud API only replies within 24 hours of the contact's last message and not to groups; marketing templates go out only through a campaign.
list_whatsapp_groups✓List the WhatsApp groups the account's own paired number belongs to, with id, name and participant count. Use a group's id as to in send_whatsapp_message, or as an attendeeIdentifiers entry in send_chat_message, to send into that group.

Admin and team

ToolReadDestructivePurpose
update_workspace_settingstarget profile updates your own name, country, and avatar; target company updates the workspace currency (admin only).
manage_teamInvite members by email (action invite, up to 20, sends real invitation emails) or change a member's role and status (action update_member).

Support

ToolReadDestructivePurpose
request_supportOpen a support ticket with the Virtual Marketer CRM team (subject plus description). The team follows up by email and in the in-app chat. Returns the ticket number.

Audit log

ToolReadDestructivePurpose
get_audit_changes✓Read the audit log as field-level changes, newest first: event, record, author, time, and per change the field, the custom column id, and the full previous and current value. Filter by user, record, event names, custom columns and a time window; page with before. Notes come back as markdown. Writes made directly in the database are not in the log. Needs audit log read.
revert_audit_entries✓Undo up to 50 audit log entries. A creation is undone by deleting the record while it still exists; an update is undone field by field, and only where the record still holds the value that entry wrote. A field changed since is left alone and reported with both values. Deletions and time tracking are refused. Covers organization, contact, deal, service and task records, their native fields and their custom column values, never their relations. Needs the same permission as the original write, plus audit log read. Pass dryRun to report without writing.

The undo is written to the audit log itself and attributed to the caller. Those entries are not revertible in turn, which is what keeps undo from looping.

Narrowing with ?toolsets=

All 62 tools are on by default. To expose only part of the surface, append ?toolsets= with comma-separated group keys to the endpoint URL:

https://crm.virtual-marketer.de/api/v1/mcp?toolsets=records,messaging

Keys: records, workspace, messaging, docs, custom-columns, widgets, webhooks, projects, campaigns, insights, teams, billing, time-tracking, enrichment, whatsapp, admin, support, audit. No parameter means everything; unknown keys are ignored. search and fetch are always on so deep-research connectors keep working on any narrowed surface.

For a key you created yourself, ?toolsets= is a convenience, not a permission: it is read from the request URL, so anything holding the key can simply leave it off and reach the whole surface. Treat every API key as full access to everything your role can do.

Keys the Virtual Marketer agent connects with

Signing into the CRM with a Virtual Marketer account provisions one API key named Virtual Marketer Agent, so the agent is wired up without anyone copying a key by hand. It appears in Profile → API & Connectors next to your own keys and can be revoked there; the next sign-in provisions a fresh one.

That key is the one case where the toolset list is enforced rather than suggested. It is bound to records, workspace, docs, enrichment and projects, and delete_records is withheld. A request from it that asks for messaging, whatsapp, admin or billing, or that omits ?toolsets= altogether, still sees only the bound groups. The binding lives with the key, not in the URL.

The bound surface is coarser than the risk it manages: toolset groups are the unit of scoping, and records carries create, update and read together. Everything the key does happens as you, under your own role, so it can reach no record your account cannot.

Safety rules baked in

  • Confirm before destructive actions. The server instructions tell the model to confirm with the user before delete_records and before any send tool.
  • Relations change only via manage_record_links. The update tools never touch relations, and null on a relationship array is rejected server-side with a remediation hint.
  • Draft, then send. send_email and send_chat_message deliver immediately. When asked to prepare a message, the agent uses save_message_draft and the user sends from the inbox. Drafts are thread-bound; a brand-new outbound message cannot be drafted.
  • Destructive flags everywhere. Every destructive tool or action has destructiveHint: true and an IRREVERSIBLE description prefix.
  • Every enum field lists its valid values inline in the description, and every filters field includes a concrete JSON example.

Next

  • Custom connector: end-to-end setup on one page.
  • Filter syntax: every operator, with examples.
  • Webhooks: the other half of the agentic loop.
When to use MCP
The endpoint
Connect a client
Server instructions, prompts, and toolsets
The Virtual Marketer Agent
Explicit rules and implicit learning
Critical review of target group and style
How the tool surface is shaped
CLI use
OpenAPI alongside MCP
Tool catalog
Records
Workspace
Messaging
Documentation and deep research
Custom columns
Widgets
Webhooks
Projects
Campaigns
Insights and follow-ups
Teams
Billing
Time tracking
Enrichment
WhatsApp
Admin and team
Support
Audit log
Narrowing with ?toolsets=
Keys the Virtual Marketer agent connects with
Safety rules baked in
Next