Developer docs

Vendo AI API and MCP server

Everything you can do in the Vendo AI app, you can also do from code. Call the REST API from your own backend, or connect Claude, ChatGPT or Cursor to our MCP server and run agents, Vendo SDR, campaigns and the CRM by simply asking.

362 REST endpoints, 396 MCP tools, 13 key scopes. Updated , see the changelog.

How it works

There are two ways in. The REST API at https://api.vendo-ai.com/v1 takes and returns JSON, and every response is wrapped in {"data": …}. The MCP server at https://api.vendo-ai.com/mcp offers the same features as tools, so an AI assistant can work in your Vendo AI account for you.

Both use the same API key. You create it in the app under Settings, API & MCP and send it as Authorization: Bearer vnd_live_…. A key belongs to one workspace: if you pass the id of something from another workspace, you get a 404.

Anything that spends credits, contacts real people or deletes data happens in two steps. First you send a dry_run to see what will happen, then the same request with confirm. In MCP that is a preview_* tool followed by the real one. Slow operations answer with 202 and a job id you can poll. Billing is read-only, and payments, buying phone numbers and deleting the account stay in the app.

AI agents

Create an agent, scan a website, generate the script, pick a voice and avatar, then publish it as a website widget, Slide Presenter, Google Meet guest, Telegram bot, phone line or WhatsApp number.

Vendo SDR

Describe your company, let it research your ideal customer, run searches with 24 signal hunters, check every contact against your ICP and start email and LinkedIn outreach.

Campaigns

Multi-channel campaigns with previews, mailboxes, LinkedIn accounts, phone calls, AI conversation threads and live stats.

CRM and audiences

Leads, pipelines, documents, messages and the calendar, plus search across the people database, custom audiences and Audience Sets.

New to Vendo AI? Start with the product overview, the Vendo SDR page and pricing. Writing code? Go to the quick start.

Quick Start

  1. Get a key. In app.vendo-ai.com › Settings → API & MCP click Create my key and copy the vnd_live_… value (shown once). Keys created there are full-access.
  2. Call the API. Base URL https://api.vendo-ai.com/v1, header Authorization: Bearer $VENDO_API_KEY. The three calls below list your agents, create a Vendo SDR search and read the contacts it finds.
  3. Or connect an AI assistant to https://api.vendo-ai.com/mcp: see MCP setup.
curl
export VENDO_API_KEY="vnd_live_YOUR_KEY"

# 1. List your agents (AI sales agents / company profiles)
curl -s https://api.vendo-ai.com/v1/agents \
  -H "Authorization: Bearer $VENDO_API_KEY"

# 2. Create a Vendo SDR search for one agent that already has company details.
#    All 24 signal hunters are enabled by default; target_regions is required.
export AGENT_ID="paste-an-agent-id"
curl -s -X POST https://api.vendo-ai.com/v1/sdr/searches \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"agent_id\": \"$AGENT_ID\", \"name\": \"UK buyers\", \"target_regions\": [\"GB\"]}"
# → {"data": {"search": {"id": "…", "status": "active", …}, "activated": true, …}}

# 3. Read the contacts the hunters found (they arrive over minutes and hours)
export SEARCH_ID="paste-data.search.id"
curl -s "https://api.vendo-ai.com/v1/sdr/people?search_id=$SEARCH_ID&limit=20" \
  -H "Authorization: Bearer $VENDO_API_KEY"
JavaScript
// Node 18+ (built-in fetch). Save as quickstart.mjs and run: node quickstart.mjs
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;

async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data;
}

// 1. List agents
const agents = await vendo("GET", "/agents");
console.log(agents.map((a) => `${a.id}  ${a.name}`));

// 2. Create an SDR search (all 24 hunters by default; target_regions required)
const created = await vendo("POST", "/sdr/searches", {
  agent_id: agents[0].id,
  name: "UK buyers",
  target_regions: ["GB"],
});
const searchId = created.search.id;

// 3. Read contacts found so far (poll again later — hunters run continuously)
const people = await vendo("GET", `/sdr/people?search_id=${searchId}&limit=20`);
console.log(people.items);
Python
# pip install requests ; export VENDO_API_KEY=vnd_live_…
import os
import requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]

# 1. List agents
agents = vendo("GET", "/agents")
for a in agents:
    print(a["id"], a["name"])

# 2. Create an SDR search (all 24 hunters by default; target_regions required)
created = vendo("POST", "/sdr/searches", {
    "agent_id": agents[0]["id"], "name": "UK buyers", "target_regions": ["GB"],
})
search_id = created["search"]["id"]

# 3. Read contacts found so far (poll again later)
people = vendo("GET", "/sdr/people", search_id=search_id, limit=20)
for p in people["items"]:
    print(p["full_name"], p["title"], p["company_name"], p.get("email"))

Creating a search is free; every usable contact a search delivers costs 5 credits (contacts the ICP check marks Not a fit are free). Use a vnd_test_… key to explore read-only first.

Authentication & scopes

API keys

Keys look like vnd_live_ + 64 hex characters (32 random bytes). Test keys use the prefix vnd_test_. Send the key on every request in either header:

http
Authorization: Bearer vnd_live_YOUR_KEY
# or
X-API-Key: vnd_live_YOUR_KEY
  • Full access by default. Keys created in Settings → API & MCP are full-access keys: they automatically receive every scope, including scopes added in later releases (for example the crm:* and workspace:* scopes).
  • Hashed storage. Only a SHA-256 hash is stored; the raw key is shown once.
  • Instant revocation. Delete a key in Settings and it fails on the next request with 401 bad_api_key.
  • Optional IP allow-list. A key with an allow-list answers 403 ip_not_allowed from other addresses.

Sandbox (test) keys

vnd_test_… keys are a read-only sandbox: GET requests, preview routes and {"dry_run": true} calls work; any real write is refused with 403 sandbox_read_only. Nobody is contacted and no credits are spent. Switch to a live key for real writes.

Scopes

Each endpoint requires one scope (shown on every endpoint card). A key without it gets 403 missing_scope. campaigns:control (launch/pause/send) is deliberately separate from campaigns:write.

ScopeAllows
agents:readAgents, channels, voices, presenter, Google Meet, Telegram, phone and WhatsApp reads, meetings calendar, previews, jobs
agents:writeCreate/update/delete agents, scans and script generation, knowledge, channels and their settings, test calls, meetings actions
analytics:readCampaign, agent and SDR analytics, live status, monitor, search stats, events and activity feeds
audiences:readPeople search preview, saved and custom audiences, Audience Sets, Vendo SDR searches, research, hunters and contacts
audiences:writeSave/upload/enrich audiences, buy Audience Sets, create and edit SDR searches, run hunters, ICP QA, contact groups
billing:readBalance, subscription, limits, pricing, transactions, invoices and credit usage (there is no billing write scope)
campaigns:controlLaunch, pause, resume, archive; send now; reply in threads and LinkedIn; take over conversations (separate scope for safety)
campaigns:readCampaigns, emails, replies, threads, LinkedIn accounts and conversations, mailboxes, SDR outreach settings, planner
campaigns:writeCreate/update/delete campaigns, prepare emails, connect mailboxes and LinkedIn accounts, SDR outreach settings and content
crm:readCRM leads, pipelines and stages, documents, communications stream, calendar, notifications, settings, lookups
crm:writeCreate/import/update leads, move between stages and pipelines, reply and email, comments, documents, stages, calendar, settings
workspace:readWorkspace and team, your profile and notification preferences, in-app notifications, CRM integrations, referral program, content accounts
workspace:writeInvite and manage members, update profile and preferences, notifications, connect/disconnect integrations and content accounts, referral links

There is intentionally no billing write scope: a leaked key can never change a plan, top up or move money.

Security model: workspace isolation

  • Every key belongs to exactly one workspace; all calls run as that workspace and any caller-supplied workspace id is ignored.
  • Every id in a request is verified against your workspace. An id from another workspace answers 404 not_found (never 403), so the API does not reveal whether another tenant's object exists.
  • Members' keys act with the member's own permissions inside the workspace (for example members see only the LinkedIn accounts they connected).
  • All REST and MCP calls are audit-logged; MCP calls are tagged X-Vendo-Via: mcp.

Conventions

Requests, responses and pagination

  • JSON request bodies (Content-Type: application/json) and JSON responses. Successful responses are wrapped: {"data": …} (a few add sibling fields such as note or summary). CSV export routes (…/export.csv) return the raw file without the envelope.
  • Errors return an HTTP status and {"code": "…", "message": "…"}.
  • Success codes: 201 only when a POST really creates something (an agent, channel, campaign, lead, stage, event, audience…); 200 for every other success, reads, PATCH, DELETE, previews, generate-*, scans, sends and state actions (launch, pause, archive, run-now…), and for any POST sent with {"dry_run": true} (nothing is created); 202 when a long job was started and is still running (see Long-running jobs). Treat any 2xx as success.
  • A request that is missing a required field never returns 2xx with an error object; it returns 400 invalid_request.
  • Ids are UUIDs unless noted (Audience Sets use a slug for reads). Timestamps are ISO-8601 UTC.
  • List endpoints that page accept limit and offset (see each endpoint's parameters; most cap limit at 100–200). Read the totals the endpoint returns (for example total) to page further.
  • Many write routes validate an explicit allow-list of body fields; an unexpected field returns 400 forbidden_field instead of being ignored.
  • Body size: up to 50 MB per request on /v1; JSON bodies up to 10 MB.

Errors

json, error
{
  "code": "missing_scope",
  "message": "Required scope(s) not on key: agents:write"
}
HTTPCodeMeaning
400invalid_requestA parameter is missing or invalid; the message names it.
400bad_requestMalformed request (for example both dry_run and confirm sent).
400forbidden_fieldThe body carries a field that cannot be set on this route (allow-listed bodies).
400invalid_parameterA path id is not a valid UUID.
400confirmation_requiredProtected action called without dry_run or confirm.
401missing_api_keyNo Authorization: Bearer or X-API-Key header.
401bad_api_keyThe key is invalid, revoked or expired.
402insufficient_creditsThe credit balance is too low for this action.
403missing_scopeThe key lacks a required scope (message lists it).
403sandbox_read_onlyA test key (vnd_test_…) attempted a real write.
403ip_not_allowedThe key has an IP allow-list and the client IP is not on it.
404not_foundThe resource does not exist or belongs to another workspace.
409conflictState conflict (for example a deep scan already running, a duplicate name).
409idempotency_conflictThe Idempotency-Key was already used for a different request.
413payload_too_largeRequest body or file over the limit.
429rate_limitedOver 60 requests/minute for this key; wait Retry-After seconds.
429too_many_failed_authToo many failed authentication attempts from this IP; retry later.
502internal_errorAn upstream service failed; safe to retry reads, use Idempotency-Key for writes.
504timeoutAn upstream call timed out.

Endpoint cards list their specific codes (for example 409 research_not_ready, 409 not_draft, 422 audience_required). Retry 429 after Retry-After; retry 5xx with backoff, using an Idempotency-Key on writes.

Rate limits

Default: 60 requests per minute per API key (sliding window). Over the limit you get 429 rate_limited.

HeaderDescription
X-RateLimit-LimitRequests allowed per window (default 60)
X-RateLimit-RemainingRequests left in the current window
Retry-AfterSeconds until you may retry (sent with 429)
X-Idempotency-Replaytrue when an Idempotency-Key matched a stored response

Over MCP, rate limiting is reported as a tool error (isError: true) inside a normal HTTP 200 JSON-RPC response, the transport call itself succeeded. Its first text line reads rate_limited, retry after N s and the JSON payload carries "code": "rate_limited" and retry_after_seconds; wait that long and call the tool again. Do not rely on an HTTP 429 from /mcp.

Repeated failed authentication from one IP is throttled separately (429 too_many_failed_auth). Some actions have product limits of their own (for example at most 5 new SDR company profiles per key per hour).

Protected actions: dry_run → confirm

Actions that delete data, spend credits or contact real people need two calls. {"dry_run": true} returns a preview (what would happen, counts, cost, blockers) and changes nothing; {"confirm": true} executes. Show the preview to a human before confirming. Sending neither flag returns 400 confirmation_required; sending both returns 400 bad_request. Test keys may always call dry_run.

bash, dry_run then confirm
# Step 1 — preview: nothing happens, you get a report
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/launch" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'
# → {"data": {"dry_run": true, "would_do": "Launch campaign \"UK SaaS founders — Q4\"",
#             "ready": true, "blockers": [], "recipient_count": 412, …}}

# Step 2 — execute after a human reviewed the preview
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/launch" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

# Neither flag → 400 {"code": "confirmation_required", …}; both → 400 bad_request
All 53 protected endpoints

In MCP the same actions are split into a preview_* tool and an execute tool, see MCP safety.

Idempotency

For any POST / PATCH / DELETE you may send an Idempotency-Key header. Replaying the same request with the same key returns the stored result without running the side effect again.

bash
curl -s -X POST https://api.vendo-ai.com/v1/campaigns \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Idempotency-Key: 6c1f3a2e-campaign-q4" \
  -H "Content-Type: application/json" \
  -d '{"name": "UK SaaS founders — Q4", "agentId": "…", "audienceId": "…"}'
# Retrying with the same key and body replays the stored response
# (header X-Idempotency-Replay: true) instead of creating a second campaign.
  • Same key + same request → stored response replayed, no side effect (X-Idempotency-Replay: true).
  • Same key + different request → 409 idempotency_conflict.
  • TTL 24 hours; keys are namespaced per API key.

Long-running jobs and polling

Some actions take minutes. They never lose their result to a client timeout:

python, job helper
import time

def run_job(agent_id, path, body):
    """POST a job route (deep-scan / generate-script) and wait for the result."""
    d = vendo("POST", f"/agents/{agent_id}/{path}", body)   # waits up to wait_seconds (default 200)
    while d.get("status") == "running":                       # HTTP 202: still running
        time.sleep(15)
        d = vendo("GET", f"/agents/{agent_id}/jobs/{d.get('job_id') or d['id']}")
    if d["status"] == "error":
        raise RuntimeError(d.get("error"))
    return d   # GET …/jobs/{id} returns the payload in d["result"]; a direct 200 spreads it into d

File uploads

File routes accept multipart/form-data or JSON with content_base64 (or image_base64 / file_base64) or a public file_url / image_url that the server fetches safely. Examples: POST /v1/audiences/custom/upload, POST /v1/crm/leads/:id/documents (field files, ≤10 files × 25 MB), POST /v1/agents/:id/documents (≤10 MB) and PATCH /v1/agents/:id/avatar (JPG/PNG/GIF ≤5 MB).

bash, three ways to upload
# multipart/form-data (file field "file")
curl -s -X POST https://api.vendo-ai.com/v1/audiences/custom/upload \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -F "file=@leads.csv" -F "name=Webinar leads"

# JSON with base64 content …
curl -s -X POST https://api.vendo-ai.com/v1/audiences/custom/upload \
  -H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json" \
  -d "{\"filename\": \"leads.csv\", \"content_base64\": \"$(base64 -w0 leads.csv)\"}"

# … or a public URL fetched server-side (SSRF-protected)
curl -s -X POST https://api.vendo-ai.com/v1/audiences/custom/upload \
  -H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json" \
  -d '{"filename": "leads.csv", "file_url": "https://example.com/exports/leads.csv"}'

Webhooks and streams → polling

This release has no webhooks and no streaming endpoints. Everything the web app streams live is available by polling: SDR activity (GET /v1/sdr/searches/:id/outreach/activity-feed, GET /v1/sdr/searches/:id/events), campaign live status (GET /v1/campaigns/:id/live-status), notifications (GET /v1/notifications/unread-count). Poll every 10–60 s and stay within the rate limit.

Status endpoints

GET https://api.vendo-ai.com/v1 is the unauthenticated version index ({"api": "Vendo AI Public API", "version": "v1", …}); GET https://api.vendo-ai.com/health returns {"status": "ok", …}. They are not counted among the 362 endpoints.

REST API reference

Every /v1 endpoint of the release, grouped by product area. Each card shows the method, path, required scope and a one-line summary; open it for the description, parameters, a copy-paste curl request, a response example, errors and the matching MCP tools. Path placeholders such as $AGENT_ID are shell variables you set yourself. Badges: new added in Round-6, updated fixed or changed in Round-6, confirm uses the dry_run → confirm protocol.

362 endpoints in 9 areas.

Agents API 99

Create AI sales/support agents, build their knowledge and script, set the voice, and put them on channels: website widget and button, Slide Presenter, Google Meet, Telegram, phone (Voice Call) and WhatsApp.

Agent core & lifecycle 13

GET/v1/agentsagents:readList all agents

list agents in the workspace of this key. List all AI agents in the account. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_agents

POST/v1/agentsagents:writeupdatedCreate an agent

Creates an agent like step 1 of the builder: name, direction (purpose), voice gender and a preset face. The agent starts in DRAFT and contacts nobody. Requires at least 1 credit on the balance (checked, not charged) and a free agent slot of the plan. Only the listed fields are accepted (metadata may only carry gender).

Parameters

NameInTypeReq.Description
namebodystringyesDisplay name (≤120)
descriptionbodystringWhat the agent does (≤2000)
purposebodystringDirection; maps to agent_type one of: sales, support, info
agent_typebodystringAlternative to purpose one of: FULL_AI_SALES_AGENT, AI_VOICE_SUPPORT_WIDGET, AI_INFO_NARRATOR
genderbodystringVoice/face gender (default female) one of: male, female
avatar_urlbodystringPreset avatar-1 … avatar-20 (default: first preset of the gender)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Anna",
  "description": "Answers questions about Acme CRM and books demos",
  "purpose": "sales",
  "gender": "female",
  "avatar_url": "avatar-2"
}'

Response

json
{
  "data": {
    "id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "name": "Anna",
    "description": "Answers questions about Acme CRM and books demos",
    "agent_type": "FULL_AI_SALES_AGENT",
    "status": "DRAFT",
    "avatar_url": "avatar-2",
    "metadata": {
      "creator": "universal",
      "gender": "female"
    },
    "company_name": null,
    "company_website": null,
    "created_at": "2026-10-26T09:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
400invalid_requestPlan limit reached ("Your STARTER plan allows up to 2 agents…") or no credits on the balance

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_agent

GET/v1/agents/plan-limitsagents:readPlan capacity vs current agent count

How many agents can the user create (base plan + purchased extra slots) vs how many they have. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/plan-limits" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agent_plan_limits

GET/v1/agents/:idagents:readGet agent details

Fetch the full details of a single agent by id.

Parameters

NameInTypeReq.Description
idpathstringyesThe agent UUID

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agent

PATCH/v1/agents/:idagents:writeupdatedUpdate agent basics (allow-listed)

Changes name, description, direction, gender or preset face. Company fields are accepted for compatibility and are written through the validated company update (read-modify-write). Any other field is rejected with forbidden_field, status, scripts, voice, slides and contact channels have their own routes.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
namebodystring
descriptionbodystring
purposebodystringone of: sales, support, info
agent_typebodystringone of: FULL_AI_SALES_AGENT, AI_VOICE_SUPPORT_WIDGET, AI_INFO_NARRATOR
genderbodystringone of: male, female
avatar_urlbodystringPreset avatar-1 … avatar-20
company_namebodystringPrefer PATCH /v1/agents/:id/company
company_websitebodystring
company_descriptionbodystring

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"purpose": "support", "gender": "male", "avatar_url": "avatar-4"}'

Response

json
{
  "data": {
    "id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "name": "Anna",
    "description": "Answers questions about Acme CRM and books demos",
    "agent_type": "AI_VOICE_SUPPORT_WIDGET",
    "status": "DRAFT",
    "avatar_url": "avatar-4",
    "metadata": {
      "creator": "universal",
      "gender": "male"
    },
    "company_name": null,
    "company_website": null,
    "created_at": "2026-10-26T09:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestNothing to update / invalid value

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_agent

DELETE/v1/agents/:idagents:writeconfirmDelete agent (requires confirm)

destructive, requires confirm/dry_run. Permanently delete an agent. NOT reversible. Only call after the user has reviewed preview_delete_agent output and explicitly approved.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_agent, delete_agent

PATCH/v1/agents/:id/avataragents:writeupdatedUpload the agent's own photo

Sets a custom avatar from a public image URL (fetched SSRF-safely) or base64. JPG, PNG or GIF up to 5 MB; replaces the preset face and deletes the previous uploaded photo.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
image_urlbodystringPublic http(s) image URL
image_base64bodystringBase64 or data: URL (instead of image_url)

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/avatar" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://cdn.acme.com/team/anna.jpg"}'

Response

json
{
  "data": {
    "id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "name": "Anna",
    "description": "Answers questions about Acme CRM and books demos",
    "agent_type": "FULL_AI_SALES_AGENT",
    "status": "DRAFT",
    "avatar_url": "/uploads/avatars/agent-4f1c9e0b7a2d3c5e6f8a9b0c1d2e3f40.jpg",
    "metadata": {
      "creator": "universal",
      "gender": "female"
    },
    "company_name": null,
    "company_website": null,
    "created_at": "2026-10-26T09:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestNo image, unsupported type (WebP/SVG) or larger than 5 MB
400forbidden_urlimage_url points to a private network

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_avatar_from_url

PATCH/v1/agents/:id/companyagents:writeSet / update the company block: name, website, description and contact_channels, see the warning below

Required before generate_agent_script will work. Set the company the agent represents, its name, website, and what it sells.

Parameters

NameInTypeReq.Description
idpathstringyes
company_namebodystringyes
company_websitebodystringyes
company_descriptionbodystringyes

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/company" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "<company_name>",
  "company_website": "<company_website>",
  "company_description": "<company_description>"
}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_company

PATCH/v1/agents/:id/contact-channelsagents:writenewSet contact channels (merge)

Sets the contacts the agent hands to visitors (also used for interactive screen cards). Merge semantics: omitted keys keep their value, "" or null clears one; custom_links replaces the list. URLs must be http(s), email and phone are validated. Refreshes the card functions of the widget/presenter voice agents.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
meeting_urlbodystringBooking link
presentation_urlbodystringDeck link
emailbodystring
phonebodystring
whatsappbodystring
telegrambodystring
custom_linksbodyarray≤10 {label (≤60), url, note? (≤200)}; null clears

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/contact-channels" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "meeting_url": "https://calendly.com/acme/demo",
  "custom_links": [
    {
      "label": "Pricing",
      "url": "https://acme.com/pricing",
      "note": "When asked about prices"
    }
  ]
}'

Response

json
{
  "data": {
    "contact_channels": {
      "meeting_url": "https://calendly.com/acme/demo",
      "email": "sales@acme.com",
      "custom_links": [
        {
          "label": "Pricing",
          "url": "https://acme.com/pricing",
          "note": "When asked about prices"
        }
      ]
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestInvalid URL / email / phone

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_contact_channels

POST/v1/agents/:id/duplicateagents:writeClone an agent

Create a copy of an existing agent (new id, same config). Counts toward agent capacity.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/duplicate" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: duplicate_agent

PATCH/v1/agents/:id/integrationsagents:writeUpdate CRM integration config

Legacy flags on the agent (zoom_enabled, google_meet_enabled, website_embed_enabled). Channels are managed with the connection tools; this does not connect anything. Free.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
zoom_enabledbodyboolean
google_meet_enabledbodyboolean
website_embed_enabledbodyboolean

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/integrations" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_integrations

PATCH/v1/agents/:id/scriptagents:writeManually overwrite script

Replace the script stages (rename, reorder, edit, add or remove stages) and optionally the tone, like the builder's script editor. Send the FULL list of stages you want; the full text is rebuilt from them. Get the current stages from get_agent (script_sections). Free.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
sectionsbodyobject[]yesAll stages in order min items 1
script_stylebodystringTone; omit to keep the current one one of: PROFESSIONAL_FORMAL, FRIENDLY_CASUAL, TECHNICAL_EXPERT, CONSULTATIVE
email_scriptbodystringWritten-channel script (email/LinkedIn); omit to keep

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/script" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sections": []}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_agent_script

PATCH/v1/agents/:id/statusagents:writeupdatedSet agent status

Sets the agent status (ACTIVE, PAUSED, DRAFT or ARCHIVED; case-insensitive). Fixed (2026-10-26): an unknown value answers 400 invalid_request (it used to reach the database and answer 500), extra body fields answer 400 forbidden_field, a foreign agent id answers 404.

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id
statusbodystringyesNew status one of: ACTIVE, PAUSED, DRAFT, ARCHIVED

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/status" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "PAUSED"}'

Response

json
{
  "data": {
    "id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "name": "Ava",
    "status": "PAUSED",
    "updated_at": "2026-10-26T14:02:11.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requeststatus is not one of ACTIVE, PAUSED, DRAFT, ARCHIVED
400forbidden_fieldBody has fields other than status
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_status

Builder: website scan, description, script 10

POST/v1/agents/page-descriptionsagents:writeAI page descriptions without an agent

Generates short AI descriptions of website pages for a site that has no agent yet. Body: website_url and pages (the page list returned by POST /v1/agents/scan).

Parameters

NameInTypeReq.Description
website_urlbodystringyesSite URL
pagesbodyobject[]yesPages from POST /v1/agents/scan

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/page-descriptions" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

POST/v1/agents/scanagents:writeupdatedQuick website scan

Lists a website's pages (path, title, description, keywords) before an agent exists. Fixed: the gateway now sends the field the backend reads (it always answered 400 "Website URL is required"). A missing website_url now answers 400 invalid_request (it used to answer 201 with an error object).

Credits: 2 credits

Parameters

NameInTypeReq.Description
website_urlbodystringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/scan" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"website_url": "https://acme.com"}'

Response

json
{
  "data": {
    "homepage": "https://acme.com",
    "pages": [
      {
        "path": "/pricing",
        "title": "Pricing",
        "description": "",
        "keywords": []
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestwebsite_url missing or empty

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: scan_website_quick

POST/v1/agents/:id/deep-scanagents:writeupdatedconfirmDeep website scan (job)

The builder's "Read my website": crawls the site and LinkedIn and extracts description, team, employee count, contacts, social links and pricing (60–180 s). Runs as a job: waits up to wait_seconds (default 200) and returns the result, or 202 with job_id, poll GET /v1/agents/:id/jobs/:jobId (kept 1 h) so a timeout never loses the paid result. persist=true (default) saves like the app: company_description replaced and save-scan-data applied (company + people records, blank contact channels filled).

Credits: 15 credits · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm
company_namebodystringDefault: agent.company_name
company_websitebodystringDefault: agent.company_website
persistbodybooleanSave the result onto the agent (default true)
auto_updatebodyobject{enabled, interval: daily|weekly|biweekly|monthly|quarterly} automatic re-scan
wait_secondsbodyinteger0–240, default 200; 0 = return the job id immediately

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/deep-scan" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirm": true,
  "company_name": "Acme",
  "company_website": "https://acme.com",
  "persist": true
}'

Response

Show the full response (53 lines)
json
{
  "data": {
    "job_id": "c2a7e4f1-6d3b-4a9e-8c5f-1e2d3c4b5a69",
    "status": "done",
    "final": {
      "companyDescription": "Acme builds a CRM for small sales teams...",
      "directors": [
        {
          "name": "John Smith",
          "position": "CEO",
          "linkedin": "https://linkedin.com/in/jsmith"
        }
      ],
      "employeeCount": "11-50",
      "contacts": {
        "addresses": [
          "1 Main St, London"
        ],
        "phones": [
          "+44 20 7946 0000"
        ],
        "emails": [
          "hello@acme.com"
        ]
      },
      "pricing": "Starter $29/mo, Pro $79/mo",
      "socialLinks": {
        "linkedin": "https://linkedin.com/company/acme",
        "facebook": "",
        "instagram": "",
        "twitter": ""
      },
      "siteStructure": []
    },
    "events": [
      {
        "step": "description",
        "status": "done",
        "data": {
          "description": "..."
        }
      }
    ],
    "persisted": {
      "company": true,
      "scan_data": {
        "companyId": "a1b2c3d4-0000-4000-8000-000000000001",
        "savedPeople": 1,
        "totalDirectors": 1
      }
    }
  }
}

Errors

202, Still running after wait_seconds: {job_id, status:'running'}

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent does not exist or is not in your workspace
400invalid_requestMissing company name/website, insufficient credits (15 required)
409conflictA deep scan is already running for this agent

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_deep_scan, deep_scan_agent, get_agent_job

POST/v1/agents/:id/generate-descriptionagents:writeupdatedDraft the company description

AI-writes "what the agent knows" from the company website without a full scan. Fixed: accepts snake_case (company_name, company_website, target_language) and falls back to the agent's saved company; ownership is checked. Not saved, pass it to PATCH /v1/agents/:id/company.

Credits: 1 credit

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
company_namebodystringDefault: agent.company_name
company_websitebodystringDefault: agent.company_website
target_languagebodystringe.g. Ukrainian

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-description" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_name": "Acme", "company_website": "acme.com"}'

Response

json
{
  "data": {
    "description": "Acme builds a CRM for small sales teams..."
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestCompany name/website missing or insufficient credits

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_agent_description

POST/v1/agents/:id/generate-page-descriptionsagents:writeAI page descriptions for an agent

Write AI descriptions + keywords for website pages (output of scan_agent_website). Costs 1 credit per 5 pages. Not saved.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
pagesbodyobject[]yesmin items 1

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-page-descriptions" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pages": []}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_agent_page_descriptions

POST/v1/agents/:id/generate-responseagents:writeGenerate a single AI reply for a given inbound message, using the agent's script and knowledge base. Useful for previewing how the agent would answer before enabling a channel

Send a message to the agent and get its reply using its current script, a text test, nobody is contacted. Needs a generated script. Use history for a multi-turn test.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
messagebodystringyesWhat the visitor says
historybodyobject[]Earlier turns, oldest first

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-response" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "<message>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: test_agent_reply

POST/v1/agents/:id/generate-scriptagents:writeupdatedGenerate the script (job)

Generates the script (stages, full text, email/LinkedIn script) in the chosen tone for the agent's direction and saves it on the agent (overwrites). Takes 60–90 s; runs as a job with wait_seconds like deep-scan, so a client timeout never loses the result. Requires company_name and company_description.

Credits: 1 credit

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
script_stylebodystringyesTone one of: PROFESSIONAL_FORMAL, FRIENDLY_CASUAL, TECHNICAL_EXPERT, CONSULTATIVE
purposebodystringDefault: from agent_type one of: sales, support, info
wait_secondsbodyinteger0–240, default 200

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-script" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"script_style": "FRIENDLY_CASUAL", "purpose": "sales"}'

Response

json
{
  "data": {
    "job_id": "c2a7e4f1-6d3b-4a9e-8c5f-1e2d3c4b5a69",
    "status": "done",
    "sections": [
      {
        "id": "greeting",
        "title": "Greeting",
        "content": "Hi! I'm Anna from Acme..."
      }
    ],
    "fullText": "[Greeting]\nHi! I'm Anna from Acme...",
    "detectedLanguage": "English",
    "emailScript": "Subject: ..."
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestCompany info missing or insufficient credits
409conflictA script job is already running

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_agent_script, get_agent_job

POST/v1/agents/:id/generate-support-scriptagents:writeGenerate support script

Legacy support-script generator of the old text-widget creators. Prefer generate_agent_script with purpose "support". Needs company name + description. Costs 1 credit.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-support-script" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_support_script

POST/v1/agents/:id/save-scan-dataagents:writePersist scan output onto the agent

Save company facts (team, contacts, social links, pricing, employee count, auto re-scan) onto the agent, what the app does after a scan. Side effects: stores them in the agent, creates/updates the company and people records, fills BLANK contact channels (never overwrites manual ones). Not needed after deep_scan_agent with persist=true. Free.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
company_namebodystringyes
company_websitebodystringyes
directorsbodyobject[]Team / management
contactsbodyobject
social_linksbodyobject
pricingbodystring
employee_countbodynumber | string
auto_updatebodyobject

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/save-scan-data" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_name": "<company_name>", "company_website": "<company_website>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: save_scan_data

POST/v1/agents/:id/scan-websiteagents:writeQuick site scan attached to an agent

List the pages of a website (path, title, description, keywords) for this agent, used by the page-navigation feature. Costs 2 credits. Read-only for the agent (nothing saved).

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
website_urlbodystringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/scan-website" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"website_url": "<website_url>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: scan_agent_website

Long-running jobs 2

GET/v1/agents/:id/jobsagents:readupdatedList recent jobs

Deep scans and script generations of this agent started through the API in the last hour (without results). Use it to recover a lost job id. Jobs live in the gateway process memory and are lost on a restart. A foreign or deleted agent id now answers 404 (it used to return an empty list).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/jobs" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "c2a7e4f1-6d3b-4a9e-8c5f-1e2d3c4b5a69",
      "kind": "deep_scan",
      "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
      "status": "running",
      "started_at": "2026-10-26T10:00:00.000Z",
      "progress": {
        "description": "done",
        "management": "scanning"
      }
    }
  ]
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_agent_jobs

GET/v1/agents/:id/jobs/:jobIdagents:readnewGet a job result

Status (running / done / error), per-step progress and the result of a deep scan or script generation job. Poll every 10–20 s while running.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
jobIdpathstring (uuid)yesFrom deep-scan / generate-script

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/jobs/$JOB_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (53 lines)
json
{
  "data": {
    "id": "c2a7e4f1-6d3b-4a9e-8c5f-1e2d3c4b5a69",
    "kind": "deep_scan",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "status": "done",
    "started_at": "2026-10-26T10:00:00.000Z",
    "finished_at": "2026-10-26T10:02:21.000Z",
    "progress": {
      "description": "done",
      "management": "done",
      "employees": "done",
      "contacts": "done",
      "pricing": "done",
      "complete": "done"
    },
    "result": {
      "final": {
        "companyDescription": "Acme builds a CRM for small sales teams...",
        "directors": [
          {
            "name": "John Smith",
            "position": "CEO",
            "linkedin": "https://linkedin.com/in/jsmith"
          }
        ],
        "employeeCount": "11-50",
        "contacts": {
          "addresses": [
            "1 Main St, London"
          ],
          "phones": [
            "+44 20 7946 0000"
          ],
          "emails": [
            "hello@acme.com"
          ]
        },
        "pricing": "Starter $29/mo, Pro $79/mo",
        "socialLinks": {
          "linkedin": "https://linkedin.com/company/acme",
          "facebook": "",
          "instagram": "",
          "twitter": ""
        },
        "siteStructure": []
      },
      "persisted": {
        "company": true
      }
    }
  }
}

Errors

HTTPCodeWhen
404not_foundJob does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agent_job

Knowledge 3

POST/v1/agents/:id/documentsagents:writenewUpload a knowledge file

Attaches a material (PDF, DOC, DOCX, TXT, XLS, XLSX, CSV, PPT, PPTX, KEY; ≤10 MB) from a public URL or base64, like the builder's "Upload materials". Used as knowledge the next time the script is generated. Free.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
filenamebodystringyesName with a supported extension
file_urlbodystringPublic http(s) URL
file_base64bodystringBase64 content (instead of file_url)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/documents" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "price-list.pdf", "file_url": "https://acme.com/files/price-list.pdf"}'

Response

json
{
  "data": {
    "url": "/uploads/documents/doc-8e1f0c2b9a7d4e3f6a5b8c9d0e1f2a3b.pdf",
    "filename": "price-list.pdf"
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestUnsupported extension, empty file or larger than 10 MB

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: add_agent_document

DELETE/v1/agents/:id/documentsagents:writeupdatedRemove a knowledge file

Removes an uploaded material from the agent. Pass url (preferred) or filename. Fixed (2026-10-26): document urls returned by GET /v1/agents/:id carry a signing query (?md5=…&expires=…) that changes on every read; it is ignored when matching, and the stored path is what is removed, the url from any earlier GET works.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
urlbody|querystringdocuments[].url from GET /v1/agents/:id
filenamebody|querystringdocuments[].filename

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID/documents" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "/uploads/documents/doc-8e1f0c2b9a7d4e3f6a5b8c9d0e1f2a3b.pdf?md5=Qm3x…&expires=1793102400"
}'

Response

json
{
  "data": {
    "removed": "/uploads/documents/doc-8e1f0c2b9a7d4e3f6a5b8c9d0e1f2a3b.pdf",
    "documents": []
  }
}

Errors

HTTPCodeWhen
404not_foundAgent or document does not exist or is not in your workspace
400invalid_requestNeither url nor filename

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: remove_agent_document

POST/v1/agents/:id/generate-block-from-urlagents:writenewKnowledge block from a web page

Reads a public web page and returns a knowledge text block about it. Not saved, append it to company_description if wanted. Private/internal addresses are refused.

Credits: 1 credit

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
urlbodystringyesPublic http(s) page

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-block-from-url" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://acme.com/features"}'

Response

json
{
  "data": {
    "content": "Acme CRM features: pipeline boards, email sync, AI lead scoring..."
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestMissing URL or insufficient credits
400forbidden_urlURL resolves to a private network

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_knowledge_from_url

Voice 4

GET/v1/agents/voices/catalogagents:readnewVoice catalogue (Voice v2)

All voices offered on the widget, presentation and Telegram voice pages, with engine, expressive and Ukrainian support and the recommended flag.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/voices/catalog" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "voice_id": "retell-Grace",
      "voice_name": "Grace",
      "gender": "female",
      "accent": "American",
      "age": "Young",
      "preview_audio_url": "https://retell-utils-public.s3.amazonaws.com/grace.mp3",
      "provider": "retell",
      "engine": "platform",
      "expressive": true,
      "ukrainian": false,
      "recommended": true
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_voice_catalog

GET/v1/agents/voices/retellagents:readList available Retell voices

Catalogue of voices that can be assigned via set_agent_voice. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/voices/retell" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_retell_voices

PATCH/v1/agents/:id/voiceagents:writeUpdate raw voice settings (legacy)

Legacy pass-through: the JSON body is forwarded unchanged to the agent's stored voice settings. Prefer PATCH /v1/agents/:id/widget-voice, which validates the fields, merges them and provisions the voice at the provider.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
(body)bodyobjectyesRaw voice settings object, forwarded as-is

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/voice" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

PATCH/v1/agents/:id/widget-voiceagents:writenewSet the website-widget voice

Exactly what the widget settings page saves: voice, pace, emotion, languages, rules, sound, behaviour, model and screen cards. Merge over the saved Voice v2 settings (defaults = the app's). Sends retell_voice_id, so the widget voice agent is created/updated at the provider and voice really works (up to ~1 min). Use PATCH /v1/agents/:id/voice only for raw backend fields.

Credits: free (widget voice calls 14 credits/min)

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
voice_idbodystringVoice from GET /v1/agents/voices/catalog (required the first time)
speedbodynumber0.7–1.3, 1 = normal
emotionbodystringDelivery one of: neutral, expressive, calm, dynamic
languagesbodystring[]Retell locales (xx-YY, ≤20). More than one = multilingual
rulesbodystringCommunication rules (≤8000 chars). Default text is generated when none is saved
ambientbodystringBackground sound one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumebodynumber0–2
backchannelbodybooleanShort acknowledgements while the person talks
backchannel_frequencybodynumber0–1
end_call_sbodynumberHang up after N s of silence, 10–120
volumebodynumber0–2
responsivenessbodynumber0–1
interruption_sensitivitybodynumber0–1
dynamic_voice_speedbodyboolean
dynamic_responsivenessbodyboolean
expressive_modebodybooleanIgnored for voices that are not expressive in the catalogue
denoising_modebodystringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsbodynumberNudge a silent person after N s; 0 = off
handbookbodyobjectBooleans: conversational_personality, natural_filler_words, high_empathy, echo_verification, speech_normalization, ai_disclosure, scope_boundaries (merged)
llmbodystringModel one of: fast, balanced, smart
screen_cardsbodybooleanInteractive cards on screen during voice calls

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/widget-voice" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "voice_id": "retell-Grace",
  "speed": 1.05,
  "emotion": "expressive",
  "languages": [
    "en-US",
    "uk-UA"
  ],
  "llm": "balanced",
  "screen_cards": true
}'

Response

json
{
  "data": {
    "voice_ready": true,
    "voice_id": "retell-Grace",
    "language": "multi",
    "communication_rules": "You are Anna, a sales representative at Acme...",
    "voice_v2": {
      "languages": [
        "en-US",
        "uk-UA"
      ],
      "volume": 1,
      "responsiveness": 0.8,
      "llm": "balanced",
      "screen_cards": true,
      "emotion": "expressive",
      "saved_at": "2026-10-26T10:12:03.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestvoice_id missing/unknown or invalid value

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_agent_voice

Channels (connections) 5

GET/v1/agents/:id/connectionsagents:readList all connections

See every channel attached to this agent: widgets, telegram, phone, presentations, etc.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_agent_connections

POST/v1/agents/:id/connectionsagents:writeupdatedAdd a channel

Adds a channel. WEBSITE_WIDGET (website_url required) starts from the app defaults (Modern design) plus your widget_config; a second widget for the same URL updates the existing one. WEBSITE_BUTTON and PRESENTATION_WIDGET (= publish the Slide Presenter) need only a name/URL. GOOGLE_MEET gets the agent's own invite address. Fields are allow-listed and validated; TELEGRAM/WHATSAPP have their own routes.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
typebodystringyesone of: WEBSITE_WIDGET, WEBSITE_BUTTON, PRESENTATION_WIDGET, GOOGLE_MEET, PHONE_CALL
namebodystring
website_urlbodystringRequired for WEBSITE_WIDGET
widget_configbodyobjectWEBSITE_WIDGET only. design classic|modern, theme{glass, glassOpacity, blur, headerStyle gradient|solid, gradient[2-3 hex], gradientAngle, accentGradient, openAnimation genie|spring|scale|slide|fade, voiceVisual portrait|liquid|waves}, position, colours (primaryColor, backgroundColor, textColor, buttonColor, buttonTextColor, headerColor, headerTextColor, userBubbleColor, userBubbleTextColor, agentBubbleColor, agentBubbleTextColor), borderRadius, logoVariant white|blue|black, launcherShape round|square, launcher_image_url | launcher_image_base64 (≤~110 KB), launcherImage "" (remove), collectName/Email/Phone, teaser, teaserDelay 3-30, autoOpen, autoOpenDelay, autoOpenSound, autoOpenMessage, replySound, unreadBadge, navigationEnabled, navigationMode auto|ask, continueAfterNavigate, texts{welcomeMessage, welcomeFollowUp, contact-form wording; ""/null = default}
google_meet_configbodyobjectGOOGLE_MEET only: auto_join, join_before_minutes 0-30, greeting_message, language, meeting_mode voice_only|presentation (needs slides). agent_email is set by the platform
phone_call_configbodyobjectPHONE_CALL only: call_script, greeting_message, call_objective, max_call_duration_seconds, post_call_actions. Number/voice/inbound → POST /v1/agents/:id/phone/setup

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "WEBSITE_WIDGET",
  "website_url": "acme.com",
  "widget_config": {
    "teaser": true,
    "collectName": true,
    "collectEmail": true,
    "texts": {
      "welcomeMessage": "Hi! I'\''m Anna from Acme."
    }
  }
}'

Response

Show the full response (40 lines)
json
{
  "data": {
    "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "type": "WEBSITE_WIDGET",
    "status": "ACTIVE",
    "name": "acme.com",
    "website_url": "https://acme.com",
    "widget_config": {
      "design": "modern",
      "position": "bottom-right",
      "primaryColor": "#3B82F6",
      "teaser": true,
      "teaserDelay": 5,
      "autoOpenMessage": "Hi! How can I help you today?",
      "collectName": true,
      "collectEmail": true,
      "theme": {
        "glass": true,
        "glassOpacity": 0.78,
        "blur": 24,
        "headerStyle": "gradient",
        "gradient": [
          "#2563EB",
          "#7C3AED"
        ],
        "gradientAngle": 135,
        "accentGradient": true,
        "openAnimation": "genie",
        "voiceVisual": "portrait"
      },
      "texts": {
        "welcomeMessage": "Hi! I'm Anna from Acme."
      },
      "launcherImage": ""
    },
    "total_conversations": 0,
    "created_at": "2026-10-26T09:05:00.000Z"
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestInvalid type/value, launcher image too large

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_agent_connection

GET/v1/agents/:id/connections/:cidagents:readupdatedGet a channel

One channel with its settings. Changed: the embedded agent object and voice-provider ids are no longer returned (voice_agent_ready / inbound_agent_ready flags instead). A deleted channel answers 404 (deleted channels used to stay addressable).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (40 lines)
json
{
  "data": {
    "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "type": "WEBSITE_WIDGET",
    "status": "ACTIVE",
    "name": "acme.com",
    "website_url": "https://acme.com",
    "widget_config": {
      "design": "modern",
      "position": "bottom-right",
      "primaryColor": "#3B82F6",
      "teaser": true,
      "teaserDelay": 5,
      "autoOpenMessage": "Hi! How can I help you today?",
      "collectName": true,
      "collectEmail": true,
      "theme": {
        "glass": true,
        "glassOpacity": 0.78,
        "blur": 24,
        "headerStyle": "gradient",
        "gradient": [
          "#2563EB",
          "#7C3AED"
        ],
        "gradientAngle": 135,
        "accentGradient": true,
        "openAnimation": "genie",
        "voiceVisual": "portrait"
      },
      "texts": {
        "welcomeMessage": "Hi! I'm Anna from Acme."
      },
      "launcherImage": ""
    },
    "total_conversations": 0,
    "created_at": "2026-10-26T09:05:00.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundAgent or channel does not exist, belongs to another workspace, or the channel was deleted

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agent_connection

PATCH/v1/agents/:id/connections/:cidagents:writeupdatedChange a channel

Changes name, website_url or the type's config. Widget settings are allow-listed and validated; texts and theme are merged with the stored ones (the backend replaces nested objects whole), launcher_image_url is converted to the stored picture. Channel status cannot be changed here. A deleted channel answers 404 (deleted channels used to stay addressable).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
namebodystring
website_urlbodystring
widget_configbodyobjectWEBSITE_WIDGET only. design classic|modern, theme{glass, glassOpacity, blur, headerStyle gradient|solid, gradient[2-3 hex], gradientAngle, accentGradient, openAnimation genie|spring|scale|slide|fade, voiceVisual portrait|liquid|waves}, position, colours (primaryColor, backgroundColor, textColor, buttonColor, buttonTextColor, headerColor, headerTextColor, userBubbleColor, userBubbleTextColor, agentBubbleColor, agentBubbleTextColor), borderRadius, logoVariant white|blue|black, launcherShape round|square, launcher_image_url | launcher_image_base64 (≤~110 KB), launcherImage "" (remove), collectName/Email/Phone, teaser, teaserDelay 3-30, autoOpen, autoOpenDelay, autoOpenSound, autoOpenMessage, replySound, unreadBadge, navigationEnabled, navigationMode auto|ask, continueAfterNavigate, texts{welcomeMessage, welcomeFollowUp, contact-form wording; ""/null = default}
google_meet_configbodyobjectGOOGLE_MEET only: auto_join, join_before_minutes 0-30, greeting_message, language, meeting_mode voice_only|presentation (needs slides). agent_email is set by the platform
phone_call_configbodyobjectPHONE_CALL only: call_script, greeting_message, call_objective, max_call_duration_seconds, post_call_actions. Number/voice/inbound → POST /v1/agents/:id/phone/setup

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "widget_config": {
    "design": "modern",
    "theme": {
      "voiceVisual": "liquid"
    },
    "launcher_image_url": "https://cdn.acme.com/anna-160.png",
    "texts": {
      "contactTitle": "Before we chat"
    }
  }
}'

Response

Show the full response (41 lines)
json
{
  "data": {
    "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "type": "WEBSITE_WIDGET",
    "status": "ACTIVE",
    "name": "acme.com",
    "website_url": "https://acme.com",
    "widget_config": {
      "design": "modern",
      "position": "bottom-right",
      "primaryColor": "#3B82F6",
      "teaser": true,
      "teaserDelay": 5,
      "autoOpenMessage": "Hi! How can I help you today?",
      "collectName": true,
      "collectEmail": true,
      "theme": {
        "glass": true,
        "glassOpacity": 0.78,
        "blur": 24,
        "headerStyle": "gradient",
        "gradient": [
          "#2563EB",
          "#7C3AED"
        ],
        "gradientAngle": 135,
        "accentGradient": true,
        "openAnimation": "genie",
        "voiceVisual": "portrait"
      },
      "texts": {
        "welcomeMessage": "Hi! I'm Anna from Acme.",
        "contactTitle": "Before we chat"
      },
      "launcherImage": "data:image/png;base64,iVBORw0KGgo..."
    },
    "total_conversations": 0,
    "created_at": "2026-10-26T09:05:00.000Z"
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
400invalid_requestConfig not valid for this channel type / invalid value
404not_foundAgent or channel does not exist, belongs to another workspace, or the channel was deleted

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_agent_connection

DELETE/v1/agents/:id/connections/:cidagents:writeupdatedconfirmRemove a channel

Soft-deletes a channel (no restore route exists). Changed: now follows the destructive protocol, dry_run shows the channel and the effect, confirm removes it (breaking for callers that sent no flag). A deleted channel answers 404 (deleted channels used to stay addressable).

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Remove channel \"acme.com\" (WEBSITE_WIDGET)",
    "effect": "The widget embed code on the site stops loading this channel.",
    "connection": {
      "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
      "type": "WEBSITE_WIDGET",
      "name": "acme.com",
      "website_url": "https://acme.com"
    },
    "reversible": false
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent or channel does not exist, belongs to another workspace, or the channel was deleted

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_agent_connection, delete_agent_connection

Website widget & button 3

GET/v1/agents/:id/connections/:cid/embed-codeagents:readupdatedEmbed code of a channel

The snippet the app copies: website widget (window.VendoAI + embed.js; settings load from the server), website button (styled by the query parameters, not stored server-side) or Slide Presenter (snippet + direct link). Replaces the legacy GET /v1/agents/:id/widget/embed-code. A deleted channel answers 404 (deleted channels used to stay addressable).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
textquerystringButton label
background_colorqueryhex
text_colorqueryhex
border_radiusqueryinteger0–50
sizequerystringone of: small, medium, large
modal_primary_colorqueryhex

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/embed-code" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "type": "WEBSITE_WIDGET",
    "embed_code": "<!-- Vendo AI Widget -->\n<script>\n  window.VendoAI = { agentId: '...', connectionId: '...' };\n</script>\n<script src=\"https://app.vendo-ai.com/widget/embed.js\" async></script>",
    "note": "Widget settings load from the server; later changes apply without touching the site."
  }
}

Errors

HTTPCodeWhen
400invalid_requestChannel type has no embed code
404not_foundAgent or channel does not exist, belongs to another workspace, or the channel was deleted

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_widget_embed_code

POST/v1/agents/:id/connections/:cid/test-pageagents:writenewCreate a widget test page

Creates (or returns, idempotent) a private, noindex test page with this website widget installed. Only the agent's creator may do this (a teammate's key gets 400).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/test-page" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Response

json
{
  "data": {
    "url": "https://report.vendo-ai.com/widget-test/acme-7f3a2c"
  }
}

Errors

HTTPCodeWhen
404not_foundChannel does not exist or is not in your workspace
400invalid_requestNot a website widget / you do not own this agent

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_widget_test_page

GET/v1/agents/:id/widget/embed-codeagents:readEmbed code for the website widget (legacy)

Legacy route kept for compatibility. Prefer GET /v1/agents/:id/connections/:cid/embed-code, which returns the snippet of a specific channel (widget, button or Slide Presenter).

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/widget/embed-code" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

Slide Presenter 14

POST/v1/agents/:id/generate-narrator-scriptagents:writeupdatedWrite a slide's presenter script

AI-writes what the presenter says on one slide. Fixed: accepts snake_case, an image by URL (fetched by the gateway, sent to the vision model) and runs with a 200 s timeout. Not saved, put it into narrator_script with POST /v1/agents/:id/presentation/slides.

Credits: 1 credit (2 with image)

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
slide_numberbodyintegeryes
total_slidesbodyintegeryes
extracted_textbodystringText on the slide
previous_scriptbodystring
next_slide_textbodystring
image_urlbodystringSlide image (vision, 2 credits)
image_base64bodystringInstead of image_url

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-narrator-script" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "slide_number": 2,
  "total_slides": 8,
  "extracted_text": "Why teams switch to Acme",
  "image_url": "https://cdn.acme.com/deck/2.png"
}'

Response

json
{
  "data": {
    "script": "Here is why teams switch to Acme: ..."
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace
400invalid_requestInsufficient credits / bad image

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_slide_script

POST/v1/agents/:id/generate-transition-phrasesagents:writeSlide transition narration

Generate the phrases the Slide Presenter uses to move between slides, in the presentation's language, and save them on the agent. Needs saved slides (set_presentation_slides).

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/generate-transition-phrases" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_transition_phrases

GET/v1/agents/:id/narrator/analyticsagents:readupdatedPresentation analytics

view=summary (default): views, presentations started, watch time, unique viewers, average duration, completion rate, recent sessions and per channel. view=period with date_from/date_to: widget loads, starts, watch minutes, unique viewers and credits. Fixed: ownership is checked and dates are forwarded.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
viewquerystringone of: summary, period
date_fromquerydate
date_toquerydate

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/narrator/analytics" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "view": "summary",
    "widgetViews": 412,
    "presentationsStarted": 96,
    "totalWatchTimeSeconds": 18230,
    "uniqueViewers": 81,
    "avgDurationSeconds": 190,
    "completionRate": 0.34,
    "recentPresentations": [
      {
        "sessionId": "s_1",
        "durationSeconds": 240,
        "startedAt": "2026-10-25T16:02:00.000Z"
      }
    ],
    "byConnection": [
      {
        "connectionId": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
        "connectionName": "acme.com",
        "views": 300,
        "plays": 70
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_narrator_analytics

GET/v1/agents/:id/narrator/leadsagents:readupdatedList presentation leads

Leads captured by the presentation call-to-action, newest first. Fixed: status, limit and offset are forwarded.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
statusquerystringone of: new, contacted, converted, spam
limitqueryinteger1–500
offsetqueryinteger

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/narrator/leads" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "leads": [
      {
        "id": "0e9d8c7b-6a5f-4e3d-8c2b-1a0f9e8d7c6b",
        "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
        "connection_id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
        "form_data": {
          "name": "Jane",
          "email": "jane@globex.com"
        },
        "page_url": "https://acme.com/demo",
        "status": "new",
        "notes": null,
        "created_at": "2026-10-25T16:10:00.000Z"
      }
    ],
    "total": 1
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_narrator_leads

PATCH/v1/agents/:id/narrator/leads/:leadIdagents:writeUpdate / dismiss a narrator lead

Set a presentation lead's status (new / contacted / converted / spam) and notes. Free.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
leadIdpathstringyes
statusbodystringone of: new, contacted, converted, spam
notesbodystring

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/narrator/leads/$LEAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_narrator_lead

DELETE/v1/agents/:id/narrator/leads/:leadIdagents:writenewconfirmDelete a presentation lead

Permanently deletes a lead (the app's trash button). dry_run shows the lead; confirm deletes it.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
leadIdpathstring (uuid)yes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID/narrator/leads/$LEAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent or lead does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_narrator_lead, delete_narrator_lead

GET/v1/agents/:id/presentationagents:readnewGet the Slide Presenter

The agent's current presentation (agent.presentation_slides, not the legacy presentations): PDF name, slides with text and presenter script (image URLs with include_images=true), look, call-to-action, presenter voice, published channels and the direct link.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
include_imagesquerybooleanAdd slide image URLs

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (56 lines)
json
{
  "data": {
    "pdf_name": "Acme pitch.pdf",
    "slides_count": 2,
    "slides": [
      {
        "id": "slide-1",
        "page_number": 1,
        "extracted_text": "Acme CRM",
        "narrator_script": "Welcome! ...",
        "has_image": true
      }
    ],
    "look": {
      "theme": "dark",
      "backgroundColor": "#0f172b",
      "buttonColor": "#3b82f6",
      "voiceEnabled": true,
      "chatEnabled": true
    },
    "look_saved": true,
    "cta": {
      "enabled": true,
      "buttonText": "Book a demo",
      "type": "link",
      "linkUrl": "https://calendly.com/acme/demo"
    },
    "voice": {
      "voice_id": "retell-Grace",
      "speed": 1.05,
      "emotion": "expressive",
      "ambient": "",
      "backchannel": true,
      "end_call_s": 30,
      "rules": "You are Anna, a sales representative at Acme...",
      "languages": [
        "en-US",
        "uk-UA"
      ],
      "volume": 1,
      "responsiveness": 0.8,
      "interruption_sensitivity": 0.5,
      "llm": "balanced",
      "screen_cards": true,
      "saved_at": "2026-10-26T10:12:03.000Z"
    },
    "published_connections": [
      {
        "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
        "type": "PRESENTATION_WIDGET",
        "name": "acme.com"
      }
    ],
    "direct_link": "https://app.vendo-ai.com/narrator-page/3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10"
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_presentation

PATCH/v1/agents/:id/presentation/ctaagents:writenewSet the call-to-action

The presentation's call-to-action: a lead form (fields) or a link. Merge over the saved CTA; fields replaces the field list. Leads appear in GET /v1/agents/:id/narrator/leads.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
enabledbodyboolean
buttonTextbodystring
typebodystringone of: form, link
linkUrlbodystringhttp(s), required for type link
linkOpenInNewTabbodyboolean
fieldsbodyarray≤12 × {id?, type text|email|phone|textarea|select, label, placeholder?, required?, options? (select)}
successMessagebodystring

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation/cta" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true,
  "type": "form",
  "buttonText": "Get a quote",
  "fields": [
    {
      "id": "name",
      "type": "text",
      "label": "Name",
      "required": true
    },
    {
      "id": "email",
      "type": "email",
      "label": "Email",
      "required": true
    }
  ]
}'

Response

json
{
  "data": {
    "cta": {
      "enabled": true,
      "buttonText": "Get a quote",
      "type": "form",
      "linkUrl": "",
      "linkOpenInNewTab": true,
      "fields": [
        {
          "id": "name",
          "type": "text",
          "label": "Name",
          "required": true
        },
        {
          "id": "email",
          "type": "email",
          "label": "Email",
          "required": true
        }
      ],
      "successMessage": "Thank you! We will contact you soon."
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestInvalid field / missing linkUrl

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_presentation_cta

GET/v1/agents/:id/presentation/embedagents:readnewPresentation embed code & link

The direct link (narrator page) and the HTML snippet with the saved look, the same snippet the app copies. Replaces the legacy /presentations/embed (old creator).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation/embed" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "direct_link": "https://app.vendo-ai.com/narrator-page/3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "embed_code": "<script>\n  window.VendoNarrator = {\n    agentId: '...',\n    ...\n  };\n</script>\n<div id=\"vendo-narrator\" style=\"width: 100%;\"></div>\n<script src=\"https://app.vendo-ai.com/widget/narrator-embed.js\" async></script>",
    "has_slides": true,
    "look_saved": true
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_presentation_embed_code

PATCH/v1/agents/:id/presentation/lookagents:writenewSet the presentation look

Theme, colours, radius, controls, autoplay, voice/chat on-off, Modern look, transition and texts. Only what you pass changes; modern and texts are merged with the saved values (the backend would replace them whole). Marks the look as saved (embed enabled).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
themebodystringone of: dark, light, custom
backgroundColorbodyhex
buttonColorbodyhex
buttonTextColorbodyhex
progressColorbodyhex
textColorbodyhex
borderRadiusbodynumber0–40
showControlsbodyboolean
autoplaybodyboolean
voiceEnabledbodyboolean
chatEnabledbodyboolean
designbodystringone of: classic, modern
modernbodyobjectbackground solid|gradient, bgFrom, bgTo, angle, glass, glassOpacity, blur, gradientButtons, accent2, panelSide right|left, presenterBubble off|left|right, font system|rounded|serif|mono, glow, voiceVisual portrait|liquid|waves
transitionbodystringone of: zoom, fade, slide
textsbodyobjectchooseMode, voice, chat, placeholder, clickToStart; ""/null = default

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation/look" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "design": "modern",
  "modern": {
    "font": "rounded",
    "voiceVisual": "liquid"
  },
  "texts": {
    "clickToStart": "Tap to start"
  }
}'

Response

json
{
  "data": {
    "look": {
      "theme": "dark",
      "backgroundColor": "#0f172b",
      "design": "modern",
      "modern": {
        "background": "gradient",
        "font": "rounded",
        "voiceVisual": "liquid"
      },
      "texts": {
        "clickToStart": "Tap to start"
      },
      "voiceEnabled": true,
      "chatEnabled": true
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestInvalid value

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_presentation_look

POST/v1/agents/:id/presentation/slidesagents:writenewSave slides (replaces the deck)

Saves the whole slide deck: add, remove, reorder, edit. Images by public URL or base64 (JPG/PNG/WebP/GIF ≤5 MB each, ≤18 MB total) are stored by the platform; omit the image of an existing slide id to keep it. Also re-detects the language and refreshes the presenter voice agent. PDF upload is not supported (the app renders PDFs in the browser), send page images.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
pdf_namebodystringPresentation name
slidesbodyarrayyes1–100 × {id?, page_number?, image_url? | image_base64?, extracted_text?, narrator_script?}

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation/slides" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "pdf_name": "Acme pitch.pdf",
  "slides": [
    {
      "id": "slide-1",
      "image_url": "https://cdn.acme.com/deck/1.png",
      "extracted_text": "Acme CRM",
      "narrator_script": "Welcome! ..."
    }
  ]
}'

Response

json
{
  "data": {
    "pdf_name": "Acme pitch.pdf",
    "slides": [
      {
        "id": "slide-1",
        "page_number": 1,
        "image_url": "https://api.vendo-ai.com/uploads/presentations/3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10/slide-1.png",
        "extracted_text": "Acme CRM",
        "narrator_script": "Welcome! ..."
      }
    ]
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestBad slide, image type or size

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_presentation_slides

PATCH/v1/agents/:id/presentation/voiceagents:writenewSet the presenter's voice

The Slide Presenter's own voice and model (the widget voice is untouched), the same settings as the app. Merge: values you omit keep the saved presentation voice (first time: the widget voice). Builds the presenter voice agent at the provider (up to ~1 min). Requires saved slides.

Credits: free (calls 17 credits/min)

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
voice_idbodystringVoice from GET /v1/agents/voices/catalog (required the first time)
speedbodynumber0.7–1.3, 1 = normal
emotionbodystringDelivery one of: neutral, expressive, calm, dynamic
languagesbodystring[]Retell locales (xx-YY, ≤20). More than one = multilingual
rulesbodystringCommunication rules (≤8000 chars). Default text is generated when none is saved
ambientbodystringBackground sound one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumebodynumber0–2
backchannelbodybooleanShort acknowledgements while the person talks
backchannel_frequencybodynumber0–1
end_call_sbodynumberHang up after N s of silence, 10–120
volumebodynumber0–2
responsivenessbodynumber0–1
interruption_sensitivitybodynumber0–1
dynamic_voice_speedbodyboolean
dynamic_responsivenessbodyboolean
expressive_modebodybooleanIgnored for voices that are not expressive in the catalogue
denoising_modebodystringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsbodynumberNudge a silent person after N s; 0 = off
handbookbodyobjectBooleans: conversational_personality, natural_filler_words, high_empathy, echo_verification, speech_normalization, ai_disclosure, scope_boundaries (merged)
llmbodystringModel one of: fast, balanced, smart
screen_cardsbodybooleanInteractive cards on screen during voice calls

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentation/voice" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "voice_id": "retell-Grace",
  "speed": 1.05,
  "emotion": "expressive",
  "languages": [
    "en-US",
    "uk-UA"
  ],
  "llm": "balanced",
  "screen_cards": true
}'

Response

json
{
  "data": {
    "voice": {
      "voice_id": "retell-Grace",
      "speed": 1.05,
      "emotion": "expressive",
      "ambient": "",
      "backchannel": true,
      "end_call_s": 30,
      "rules": "You are Anna, a sales representative at Acme...",
      "languages": [
        "en-US",
        "uk-UA"
      ],
      "volume": 1,
      "responsiveness": 0.8,
      "interruption_sensitivity": 0.5,
      "llm": "balanced",
      "screen_cards": true,
      "saved_at": "2026-10-26T10:12:03.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestUpload the presentation first / unknown voice_id / invalid value

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_presentation_voice

GET/v1/agents/:id/presentationsagents:readupdatedPresentation (legacy alias)

Legacy route kept for older clients. Fixed (2026-10-26): it read the retired presentation-creator record and answered 404 for every agent, also agents with slides. It is now an alias of GET /v1/agents/:id/presentation and returns the agent's current Slide Presenter deck (slides_count 0 when none is uploaded) plus a note. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id
include_imagesquerybooleanAdd slide image URLs

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentations" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (46 lines)
json
{
  "data": {
    "pdf_name": "Pitch deck.pdf",
    "slides_count": 2,
    "slides": [
      {
        "id": "slide-1",
        "page_number": 1,
        "extracted_text": "Vendo AI — AI sales agents",
        "narrator_script": "Hi, I am Ava…",
        "has_image": true
      },
      {
        "id": "slide-2",
        "page_number": 2,
        "extracted_text": "Pricing",
        "narrator_script": "Plans start at…",
        "has_image": true
      }
    ],
    "look": {
      "theme": "dark",
      "design": "modern"
    },
    "look_saved": true,
    "cta": {
      "enabled": true,
      "type": "form",
      "buttonText": "Book a demo"
    },
    "voice": {
      "voice_id": "retell-Grace",
      "speed": 1
    },
    "published_connections": [
      {
        "id": "d2f8a1c4-6b3e-4d9a-8f7c-1e5b0a9d3c62",
        "type": "PRESENTATION_WIDGET",
        "name": "Slide Presenter",
        "status": "ACTIVE"
      }
    ],
    "direct_link": "https://app.vendo-ai.com/narrator-page/a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71"
  },
  "note": "Alias of GET /v1/agents/:id/presentation (the current Slide Presenter). Prefer that route."
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_agent_presentations

GET/v1/agents/:id/presentations/embedagents:readupdatedPresentation embed code (legacy alias)

Legacy route kept for older clients. Fixed (2026-10-26): always answered 404; now an alias of GET /v1/agents/:id/presentation/embed (direct link + embed snippet of the current Slide Presenter). Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/presentations/embed" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "direct_link": "https://app.vendo-ai.com/narrator-page/a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "embed_code": "<script>\n  window.VendoNarrator = { agentId: 'a91d4f60-…', … };\n</script>\n<div id=\"vendo-narrator\" style=\"width: 100%;\"></div>\n<script src=\"https://app.vendo-ai.com/widget/narrator-embed.js\" async></script>",
    "has_slides": true,
    "look_saved": true
  },
  "note": "Alias of GET /v1/agents/:id/presentation/embed. Prefer that route."
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

Google Meet 11

GET/v1/agents/:id/google-meetagents:readupdatedGoogle Meet settings and status

Google Meet status and settings of the agent: invite address (agent_email), connected, record status, auto-join config and counters. Fixed (2026-10-26): connected is false once the Meet record is DISCONNECTED (after DELETE /v1/agents/:id/google-meet it used to keep saying connected: true); a foreign or deleted agent id answers 404 (it answered 200 {connected:false}).

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "connected": true,
    "id": "6e2b9c4a-3f1d-4a8e-b7c5-9d0f2e1a8b36",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "agent_email": "agent-a91d4f60@meet.vendo-ai.com",
    "status": "ACTIVE",
    "google_account_email": null,
    "config": {
      "auto_join": true,
      "join_before_minutes": 1,
      "language": "en",
      "meeting_mode": "voice_only",
      "greeting_message": "Hi all, I am Ava."
    },
    "total_meetings": 6,
    "total_minutes": 142
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_google_meet_settings

DELETE/v1/agents/:id/google-meetagents:writeupdatedconfirmDisconnect Google Meet

Disconnects Google Meet like the app: removes the GOOGLE_MEET channel(s), turns auto-join off and marks the agent's Meet record DISCONNECTED, so GET /v1/agents/:id/google-meet then reports connected: false and the bot stops joining. Destructive protocol (dry_run / confirm). Scheduled meetings are not cancelled. Reconnect any time with POST /v1/agents/:id/google-meet/connect (re-activates the record).

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "ok": true,
    "removed_channels": [
      "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73"
    ],
    "connected": false
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_google_meet, disconnect_google_meet

GET/v1/agents/:id/google-meet/analyticsagents:readnewGoogle Meet analytics

Meetings, minutes, completed, missed and credits for a date range (the Meet channel page).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
date_fromquerydate
date_toquerydate

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/analytics" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "totalMeetings": 4,
    "totalMinutes": 96,
    "completedMeetings": 3,
    "missedMeetings": 1,
    "creditsUsed": 1632,
    "breakdown": {
      "voiceCredits": 1632
    }
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_google_meet_analytics

GET/v1/agents/:id/google-meet/auth-urlagents:writeStart Google Meet OAuth (returns URL)

Returns a Google OAuth URL. A person must open it in a browser and complete the consent screen; the Google Meet connection becomes active afterwards.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/auth-url" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

PATCH/v1/agents/:id/google-meet/configagents:writeupdatedChange Google Meet settings

Auto-join, minutes before start, greeting, language, meeting mode. Fixed: allow-listed (the backend merged any key), presentation mode checked against the slides, and saved through the channel so both copies stay in sync.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
auto_joinbodyboolean
join_before_minutesbodynumber
greeting_messagebodystring
languagebodystring
meeting_modebodystringone of: voice_only, presentation

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/config" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"join_before_minutes": 2}'

Response

json
{
  "data": {
    "connected": true,
    "agent_email": "agent-3f6b2c1e@meet.vendo-ai.com",
    "status": "ACTIVE",
    "config": {
      "auto_join": true,
      "join_before_minutes": 2,
      "language": "en",
      "meeting_mode": "voice_only"
    },
    "total_meetings": 4,
    "total_minutes": 96
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent or Google Meet connection does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_google_meet_settings

POST/v1/agents/:id/google-meet/connectagents:writenewConnect Google Meet (invite address)

The app's current flow: no Google login, the agent gets its own address agent-<8>@meet.vendo-ai.com; invite it to a Google Calendar event with a Meet link and the agent joins. Creates or updates the GOOGLE_MEET channel with the settings.

Credits: free (meeting minutes billed)

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
auto_joinbodybooleanDefault true
join_before_minutesbodynumber0–30, default 1
greeting_messagebodystring
languagebodystringDefault en
meeting_modebodystringpresentation needs slides one of: voice_only, presentation

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "greeting_message": "Hello! I'\''m Anna, Acme'\''s AI assistant.",
  "meeting_mode": "voice_only"
}'

Response

json
{
  "data": {
    "connection": {
      "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
      "type": "GOOGLE_MEET",
      "name": "Google Meet",
      "status": "ACTIVE",
      "google_meet_config": {
        "auto_join": true,
        "join_before_minutes": 1,
        "greeting_message": "Hello! I'm Anna, Acme's AI assistant.",
        "language": "en",
        "meeting_mode": "voice_only",
        "agent_email": "agent-3f6b2c1e@meet.vendo-ai.com"
      }
    },
    "invite_address": "agent-3f6b2c1e@meet.vendo-ai.com",
    "how_it_works": "Invite agent-3f6b2c1e@meet.vendo-ai.com to any Google Calendar event with a Google Meet link; the agent joins 1 min before start."
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestpresentation mode without slides

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_google_meet

GET/v1/agents/:id/google-meet/meetingsagents:readList meetings

Read-only list of meetings the agent has scheduled. Requires Google Meet connected first.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/meetings" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_google_meet_meetings

GET/v1/agents/:id/google-meet/meetings/historyagents:readnewMeeting history

All meetings of this agent, newest first, paged.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
pagequeryinteger
limitqueryinteger1–200

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/meetings/history" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "meetings": [
      {
        "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
        "title": "Acme × Globex intro",
        "description": null,
        "start_time": "2026-10-28T14:00:00.000Z",
        "end_time": "2026-10-28T14:30:00.000Z",
        "meet_link": "https://meet.google.com/abc-defg-hij",
        "organizer_email": "ceo@globex.com",
        "organizer_name": "Jane Doe",
        "attendees": [
          "ceo@globex.com",
          "agent-3f6b2c1e@meet.vendo-ai.com"
        ],
        "status": "completed",
        "connection_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
        "has_transcript": false,
        "agent_in_call": false,
        "duration_minutes": 28
      }
    ],
    "total": 4,
    "page": 1,
    "limit": 20
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_google_meet_history

GET/v1/agents/:id/google-meet/meetings/:meetingIdagents:readnewGet a meeting of the agent

One meeting of this agent with transcript, summary and action items. 404 for meetings of other agents or users.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
meetingIdpathstring (uuid)yes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/meetings/$MEETING_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
    "title": "Acme × Globex intro",
    "description": null,
    "start_time": "2026-10-28T14:00:00.000Z",
    "end_time": "2026-10-28T14:30:00.000Z",
    "meet_link": "https://meet.google.com/abc-defg-hij",
    "organizer_email": "ceo@globex.com",
    "organizer_name": "Jane Doe",
    "attendees": [
      "ceo@globex.com",
      "agent-3f6b2c1e@meet.vendo-ai.com"
    ],
    "status": "scheduled",
    "connection_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "has_transcript": false,
    "agent_in_call": false,
    "transcript": {
      "messages": []
    },
    "summary": null,
    "action_items": null
  }
}

Errors

HTTPCodeWhen
404not_foundMeeting does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_meeting

GET/v1/agents/:id/google-meet/meetings/:meetingId/transcriptagents:readnewMeeting transcript

Transcript messages, summary and action items (from the database, else from the recording service).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
meetingIdpathstring (uuid)yes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/meetings/$MEETING_ID/transcript" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
    "title": "Acme × Globex intro",
    "start_time": "2026-10-28T14:00:00.000Z",
    "end_time": "2026-10-28T14:30:00.000Z",
    "duration_minutes": 28,
    "messages": [
      {
        "speaker": "Jane Doe",
        "content": "Can you show pricing?",
        "timestamp": "2026-10-28T14:03:10.000Z",
        "is_agent": false
      }
    ],
    "summary": "Intro call...",
    "action_items": [
      "Send proposal"
    ],
    "source": "database"
  }
}

Errors

HTTPCodeWhen
404not_foundMeeting does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_meeting_transcript

POST/v1/agents/:id/google-meet/syncagents:writeupdatedSync Google Calendar meetings (OAuth flow)

Only for agents connected through the old Google Calendar OAuth flow (GET /v1/agents/:id/google-meet/auth-url): pulls new, changed and deleted calendar events into the meetings list. Answers 200. Fixed (2026-10-26): without Google OAuth it answers 409 not_connected (it answered 401 unauthorized, which reads as a bad API key, or 2xx {error}); a foreign agent id answers 404.

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/google-meet/sync" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "synced": 2,
    "meetings": [
      {
        "id": "0c7e4b2a-9d1f-4e6a-8b3c-5f2d1a0e9c47",
        "title": "Demo with Northgate Dental",
        "start_time": "2026-10-28T10:00:00.000Z",
        "meet_link": "https://meet.google.com/abc-defg-hij",
        "status": "scheduled"
      }
    ]
  }
}

Errors

HTTPCodeWhen
409not_connectedThe agent has no Google Calendar OAuth connection
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

Telegram 10

GET/v1/agents/:id/telegramagents:readCurrent bot info

See the connected bot username + settings.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_telegram_bot_info

DELETE/v1/agents/:id/telegramagents:writeupdatedconfirmDisconnect the Telegram bot

Detaches the bot from the agent: the webhook is removed and the bot stops answering (it keeps existing in Telegram; chat history is kept). Changed (2026-10-26): destructive protocol, {"dry_run": true} shows the bot (username, chats, messages) without changing anything; {"confirm": true} disconnects.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Disconnect Telegram bot @acme_sales_bot from agent \"Ava\": the webhook is removed and the bot stops answering.",
    "bot": {
      "username": "acme_sales_bot",
      "first_name": "Acme Sales",
      "total_chats": 14,
      "total_messages": 233
    },
    "reversible": true,
    "undo": "POST /v1/agents/:id/telegram/connect with the same bot_token (chat history is kept)."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
404not_foundAgent does not exist, is not in your workspace, or has no bot

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_telegram_bot, disconnect_telegram_bot

GET/v1/agents/:id/telegram/analyticsagents:readupdatedTelegram analytics

Chats, messages, voice calls, unique users and credits. Fixed: date_from / date_to are forwarded.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
date_fromquerydate
date_toquerydate

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/analytics" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "totalChats": 31,
    "totalMessages": 402,
    "voiceCalls": 6,
    "textMessages": 396,
    "uniqueUsers": 29,
    "creditsUsed": 210,
    "breakdown": {
      "textCredits": 120,
      "voiceCredits": 90
    }
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_telegram_analytics

GET/v1/agents/:id/telegram/chatsagents:readnewList Telegram chats

People and groups talking to the bot, paged.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
pagequeryinteger
limitqueryinteger1–200

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/chats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "chats": [
      {
        "id": "1b2c3d4e-5f60-4718-9a0b-c1d2e3f40516",
        "chatId": "512345678",
        "chatType": "private",
        "username": "jane_d",
        "firstName": "Jane",
        "lastName": "Doe",
        "isActive": true,
        "messageCount": 14,
        "lastMessageAt": "2026-10-26T08:40:00.000Z",
        "createdAt": "2026-10-20T12:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_telegram_chats

GET/v1/agents/:id/telegram/chats/:chatIdagents:readnewRead a Telegram chat

Messages of one chat incl. voice-call turns with recording URL and duration.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
chatIdpathstring (uuid)yeschats[].id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/chats/$CHAT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "chat": {
      "id": "1b2c3d4e-5f60-4718-9a0b-c1d2e3f40516",
      "username": "jane_d",
      "firstName": "Jane"
    },
    "messages": [
      {
        "id": "m1",
        "role": "user",
        "type": "text",
        "content": "Hi",
        "createdAt": "2026-10-26T08:39:00.000Z",
        "callId": null,
        "recordingUrl": null,
        "durationSeconds": null
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundAgent or chat does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_telegram_chat

POST/v1/agents/:id/telegram/chats/:chatId/sendagents:writenewconfirmSend an operator message

Human takeover: sends a REAL Telegram message from the bot to this person. dry_run shows the recipient and text; confirm sends.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
chatIdpathstring (uuid)yes
messagebodystringyes1–4096 chars
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/chats/$CHAT_ID/send" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Hi Jane, Mark here from Acme — I'\''ll call you at 3pm.", "confirm": true}'

Response

json
{
  "data": {
    "success": true,
    "message": {
      "id": "m2",
      "role": "assistant",
      "type": "text",
      "content": "Hi Jane, Mark here from Acme — I'll call you at 3pm.",
      "createdAt": "2026-10-26T09:00:00.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent or chat does not exist or is not in your workspace
400invalid_requestEmpty message / bot not active

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_send_telegram_message, send_telegram_message

POST/v1/agents/:id/telegram/connectagents:writeupdatedConnect a Telegram bot

Connects a bot created with @BotFather (bot_token) to the agent: the webhook is set and the agent answers every chat of the bot. Answers 201. Fixed (2026-10-26): a missing or malformed bot_token answers 400 invalid_request (it answered 201 with an error object). Optional {"dry_run": true} validates the token with Telegram and shows the bot and whether it replaces the current one, nothing is connected (HTTP 200).

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id
bot_tokenbodystringyesHTTP API token from @BotFather, e.g. 123456789:AA…
dry_runbodybooleantrue = validate and preview only

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"bot_token": "123456789:AAH4k…"}'

Response

json
{
  "data": {
    "connected": true,
    "bot_username": "acme_sales_bot",
    "bot_first_name": "Acme Sales",
    "webhook_set": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestbot_token missing or not a Telegram token
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_telegram_bot

POST/v1/agents/:id/telegram/reconfigureagents:writeReconfigure bot settings

Re-apply the bot's commands and menu button (for private and group chats), use when the bot menu looks wrong. Free.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/reconfigure" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: reconfigure_telegram_bot

PATCH/v1/agents/:id/telegram/voiceagents:writeupdatedVoice calls on/off

Shows or hides the bot's VOICE button. Fixed: the backend reads voice_enabled, the old route sent enabled, which removed the VOICE button while voice stayed on. Accepts enabled or voice_enabled.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
enabledbodybooleanyesOr voice_enabled

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/voice" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

Response

json
{
  "data": {
    "success": true,
    "voice_enabled": false
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent or bot does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_telegram_voice_calls

PATCH/v1/agents/:id/telegram/voice-settingsagents:writenewTelegram voice settings

The Telegram bot's own voice (separate from the widget): the full Voice v2 set plus the mini app look. Merge over the saved Telegram voice. Creates/updates the Telegram voice agent at the provider (up to ~1 min). Needs a connected bot.

Credits: free (calls billed per minute)

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
voice_idbodystringVoice from GET /v1/agents/voices/catalog (required the first time)
speedbodynumber0.7–1.3, 1 = normal
emotionbodystringDelivery one of: neutral, expressive, calm, dynamic
languagesbodystring[]Retell locales (xx-YY, ≤20). More than one = multilingual
rulesbodystringCommunication rules (≤8000 chars). Default text is generated when none is saved
ambientbodystringBackground sound one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumebodynumber0–2
backchannelbodybooleanShort acknowledgements while the person talks
backchannel_frequencybodynumber0–1
end_call_sbodynumberHang up after N s of silence, 10–120
volumebodynumber0–2
responsivenessbodynumber0–1
interruption_sensitivitybodynumber0–1
dynamic_voice_speedbodyboolean
dynamic_responsivenessbodyboolean
expressive_modebodybooleanIgnored for voices that are not expressive in the catalogue
denoising_modebodystringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsbodynumberNudge a silent person after N s; 0 = off
handbookbodyobjectBooleans: conversational_personality, natural_filler_words, high_empathy, echo_verification, speech_normalization, ai_disclosure, scope_boundaries (merged)
llmbodystringModel one of: fast, balanced, smart
screen_cardsbodybooleanInteractive cards on screen during voice calls
visualbodystringMini app look one of: portrait, liquid, waves

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/agents/$AGENT_ID/telegram/voice-settings" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "voice_id": "retell-Grace",
  "speed": 1.05,
  "emotion": "expressive",
  "languages": [
    "en-US",
    "uk-UA"
  ],
  "llm": "balanced",
  "screen_cards": true,
  "visual": "waves"
}'

Response

json
{
  "data": {
    "success": true,
    "voice_agent_ready": true,
    "voice": {
      "voice_id": "retell-Grace",
      "speed": 1.05,
      "emotion": "expressive",
      "ambient": "",
      "backchannel": true,
      "end_call_s": 30,
      "rules": "You are Anna, a sales representative at Acme...",
      "languages": [
        "en-US",
        "uk-UA"
      ],
      "volume": 1,
      "responsiveness": 0.8,
      "interruption_sensitivity": 0.5,
      "llm": "balanced",
      "screen_cards": true,
      "saved_at": "2026-10-26T10:12:03.000Z",
      "visual": "waves"
    }
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent does not exist or is not in your workspace
400invalid_requestConnect the bot first / unknown voice_id

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_telegram_voice_settings

Voice Call (phone) 6

GET/v1/agents/:id/connections/:cid/phone-statsagents:readnewVoice Call channel counters

Calls attempted, completed, successful, minutes and credits of one PHONE_CALL channel.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/phone-stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 42,
    "attempted": 42,
    "completed": 30,
    "successful": 9,
    "total_minutes": 61,
    "total_credits": 1525
  }
}

Errors

HTTPCodeWhen
404not_foundChannel does not exist or is not in your workspace
400invalid_requestNot a PHONE_CALL channel

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_phone_channel_stats

GET/v1/agents/:id/phone/callsagents:readnewPhone calls of the agent

Calls of this agent (campaign, test and inbound) with transcript, recording URL, summary, outcome, sentiment, duration and credits, plus totals.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
limitqueryinteger1–500, default 100

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/phone/calls" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (33 lines)
json
{
  "data": {
    "stats": {
      "total_calls": 12,
      "answered_calls": 9,
      "total_minutes": 21,
      "total_credits": 525,
      "by_outcome": {
        "interested": 3
      },
      "by_sentiment": {
        "positive": 5
      }
    },
    "calls": [
      {
        "id": "e1d2c3b4-a596-4877-8695-a4b3c2d1e0f9",
        "is_test_call": true,
        "is_inbound": false,
        "contact_phone": "+447700900123",
        "status": "completed",
        "call_outcome": "interested",
        "call_sentiment": "positive",
        "call_duration_seconds": 84,
        "transcript": "Agent: Hello!...",
        "recording_url": "https://dxc03zgurdly9.cloudfront.net/rec_xyz.wav",
        "summary": "Interested, wants a demo",
        "credits_used": 37,
        "created_at": "2026-10-26T08:00:00.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_phone_calls

POST/v1/agents/:id/phone/generate-scriptagents:writenewDraft a call script

Builds a call script and greeting from the agent's sales script for a call objective, in the agent's language. Free; not saved.

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
call_objectivebodystringDefault book_meeting one of: sell, inform, book_meeting, collect_info, qualify_lead
voice_genderbodystringone of: male, female

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/phone/generate-script" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"call_objective": "book_meeting"}'

Response

json
{
  "data": {
    "script": "You are Anna, a professional saleswoman for Acme...",
    "greeting": "Hello! This is Anna from Acme. Do you have a moment to talk?"
  }
}

Errors

HTTPCodeWhen
404not_foundAgent does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: generate_phone_script

POST/v1/agents/:id/phone/setupagents:writeupdatedconfirmSet up the Voice Call channel

Mirrors the app's Voice Call wizard: attaches one of YOUR numbers, saves the PHONE_CALL channel, creates/updates the outbound (and, with accept_incoming, inbound) voice agent at the provider and links the number. Script is generated when omitted. Omitted values keep the channel's saved ones. Up to ~1 min. Changed (2026-10-26): attaches a real number and creates voice agents, so it follows the destructive protocol, {"dry_run": true} returns what would be created/updated (channel, number, objective, voice, inbound on/off) without touching anything; {"confirm": true} runs the setup. The dry run answers {"data": {"dry_run": true, "would_do", "channel": {"id", "action": "create|update"}, "phone_number": {"id", "phone_number"}, "call_objective", "call_script", "voice", "accept_incoming", "max_call_duration_seconds", "cost", "reversible", "undo"}}.

Credits: free (calls 25 credits/min) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
phone_number_idbodystring (uuid)Required the first time (GET /v1/phone/numbers)
connection_idbodystring (uuid)Existing PHONE_CALL channel
call_objectivebodystringone of: sell, inform, book_meeting, collect_info, qualify_lead
call_scriptbodystring
greeting_messagebodystring
voice_idbodystring
speedbodynumber0.7–1.3
emotionbodystringone of: neutral, expressive, calm, dynamic
languagebodystringone of: auto, multi
communication_rulesbodystring
voice_genderbodystringone of: male, female
accept_incomingbodybooleanAnswer calls to the number
ambient_soundbodystringone of: "", coffee-shop, call-center, convention-hall
enable_backchannelbodyboolean
end_call_silence_secondsbodynumber10–120, default 30
reminder_trigger_secondsbodynumber5–60, default 10
max_call_duration_secondsbodynumber30–1800, default 300
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/phone/setup" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone_number_id": "4c8e2a6f-1d3b-4f7a-9e5c-0b2d8f6a4c13",
  "call_objective": "book_meeting",
  "accept_incoming": true,
  "dry_run": true
}'

Response

json
{
  "data": {
    "connection": {
      "id": "b7e2d9a4-1c3f-4e8a-9d6b-2f5c8a1e4d73",
      "type": "PHONE_CALL",
      "name": "Phone Call Outreach",
      "phone_call_config": {
        "phone_number": "+14155550123",
        "call_objective": "book_meeting",
        "accept_incoming": true,
        "voice_id": "11labs-Adrian",
        "voice_agent_ready": true,
        "inbound_agent_ready": true
      }
    },
    "voice_agent_ready": true,
    "inbound_enabled": true,
    "phone_number": "+14155550123"
  }
}

Errors

HTTPCodeWhen
400forbidden_fieldBody contains a field that is not settable here
404not_foundAgent, channel or phone number does not exist or is not in your workspace
400confirmation_requiredNeither dry_run nor confirm was sent (or both)

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_setup_phone_agent, setup_phone_agent

POST/v1/agents/:id/phone/test-callagents:writenewconfirmTest call (real phone)

Dials a REAL phone: the agent calls to_number from the channel's number (max 2 minutes, billed 25 credits/min per started 10 s). Requires a completed phone setup with a number you own. dry_run / confirm.

Credits: ≤50 credits (25/min) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
to_numberbodystringyesE.164
connection_idbodystring (uuid)PHONE_CALL channel
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/phone/test-call" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to_number": "+447700900123", "dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Call +447700900123 from +14155550123 with this agent (max 2 minutes)",
    "estimated": {
      "max_minutes": 2,
      "credits_per_minute": 25,
      "max_credits": 50
    },
    "reversible": false
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundAgent or channel does not exist or is not in your workspace
400invalid_requestBad number / phone setup missing

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_phone_test_call, phone_test_call

GET/v1/phone/numbersagents:readnewList my phone numbers

Active phone numbers already on your account (per user). Buying numbers is not available in the API (owner decision), buy in the app.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/phone/numbers" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "6f5e4d3c-2b1a-4098-8776-655443322110",
      "phone_number": "+14155550123",
      "area_code": "415",
      "nickname": null,
      "status": "ACTIVE",
      "country": "US",
      "provider": "retell",
      "number_type": "local",
      "monthly_cost_credits": 200,
      "call_rate_credits_per_minute": 25,
      "next_billing_at": "2026-11-20T00:00:00.000Z",
      "created_at": "2026-10-20T00:00:00.000Z"
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_phone_numbers

WhatsApp 3

GET/v1/whatsapp/accountsagents:readnewList WhatsApp accounts

WhatsApp accounts connected to your user. Account-level: the backend does not tie them to one agent (auto-replies use your latest campaign's agent).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/whatsapp/accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "2a3b4c5d-6e7f-4809-9a1b-2c3d4e5f6a7b",
      "phone_number": "+447700900456",
      "display_name": "Acme Sales",
      "status": "ok",
      "created_at": "2026-10-21T10:00:00.000Z"
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_whatsapp_accounts

POST/v1/whatsapp/accounts/connectagents:writeupdatedconfirmGet a WhatsApp connect link

Returns a one-time hosted link; a human opens it and scans the QR code with WhatsApp (Linked devices), which links that real number to the workspace. Poll GET /v1/whatsapp/accounts until status is ok. Changed (2026-10-26): dry_run / confirm, {"dry_run": true} explains the effect and lists the accounts already connected without creating a link; {"confirm": true} returns the link.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/whatsapp/accounts/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "url": "https://account.unipile.com/hosted/abc123",
    "how_to": "Open the URL in a browser and scan the QR code with WhatsApp..."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
400forbidden_fieldBody has fields other than dry_run / confirm

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_connect_whatsapp_account, connect_whatsapp_account

DELETE/v1/whatsapp/accounts/:accountIdagents:writenewconfirmDisconnect a WhatsApp account

Disconnects and deletes the account at the provider; auto-replies stop and reconnecting needs a new QR scan. dry_run / confirm.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
accountIdpathstring (uuid)yes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/whatsapp/accounts/$ACCOUNT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundWhatsApp account does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_whatsapp_account, disconnect_whatsapp_account

Conversations & recordings 5

GET/v1/agents/:id/connections/:cid/conversationsagents:readupdatedList conversations (paged)

Conversations of a widget/presentation channel with messages, mode, visitor contact and page. Fixed: page and limit (1–500) are forwarded (was always the first 20).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
pagequeryintegerDefault 1
limitqueryinteger1–500, default 20

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/conversations" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "conversations": [
      {
        "id": "7c6b5a49-3827-4615-a504-f3e2d1c0b9a8",
        "mode": "voice",
        "visitor_name": "Jane",
        "visitor_email": "jane@globex.com",
        "page_url": "https://acme.com/pricing",
        "messages": [
          {
            "role": "user",
            "content": "How much is Pro?"
          }
        ],
        "created_at": "2026-10-25T15:00:00.000Z"
      }
    ],
    "total": 57,
    "page": 1,
    "limit": 20
  }
}

Errors

HTTPCodeWhen
404not_foundChannel does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_connection_conversations

GET/v1/agents/:id/connections/:cid/conversations/:conversationId/recordingagents:readnewVoice recordings of a conversation

Recording URLs (WAV on the voice provider's CDN), duration and start of the voice calls in one conversation (up to 10 calls; cached 10 min).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
conversationIdpathstring (uuid)yes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/conversations/$CONVERSATION_ID/recording" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "conversation_id": "7c6b5a49-3827-4615-a504-f3e2d1c0b9a8",
    "recordings": [
      {
        "url": "https://dxc03zgurdly9.cloudfront.net/rec_abc.wav",
        "duration_ms": 95000,
        "started_at": "2026-10-25T15:01:10.000Z",
        "status": "ended"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundChannel or conversation does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_conversation_recordings

GET/v1/linkedin/conversationscampaigns:readnewLinkedIn conversations

LinkedIn chats of the workspace, optionally of one campaign and/or account, with last message, pending AI reply, takeover flag and outreach status.

Parameters

NameInTypeReq.Description
campaign_idqueryuuid
account_idqueryuuid
limitqueryinteger1–100 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/linkedin/conversations" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "conversations": [
      {
        "chat_id": "Xk3pQ9vR2sT8uY1w",
        "contact_name": "James Whitfield",
        "contact_provider_id": "ACoAAB12cdE",
        "last_message": "Sounds good, Thursday works",
        "last_message_at": "2026-10-26T11:02:00.000Z",
        "message_count": 5,
        "has_pending_auto_reply": true,
        "last_sender": "contact",
        "outreach_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "company_name": "Brightlane",
        "outreach_status": "replied",
        "human_takeover": false,
        "contact_linkedin_url": "https://www.linkedin.com/in/jwhitfield"
      }
    ],
    "total": 27
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or account not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_linkedin_conversations

GET/v1/linkedin/conversations/:chatId/messagescampaigns:readnewMessages of a LinkedIn chat

All messages of one chat, oldest first. chatId is the provider chat id from the conversations list (not a UUID).

Parameters

NameInTypeReq.Description
chatIdpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/linkedin/conversations/$CHAT_ID/messages" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "chat_id": "Xk3pQ9vR2sT8uY1w",
      "text": "Thanks for connecting, James!",
      "sender_type": "self",
      "sent_by": "ai",
      "message_timestamp": "2026-10-25T09:00:00.000Z"
    },
    {
      "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
      "chat_id": "Xk3pQ9vR2sT8uY1w",
      "text": "Sounds good, Thursday works",
      "sender_type": "contact",
      "sender_name": "James Whitfield",
      "message_timestamp": "2026-10-26T11:02:00.000Z"
    }
  ]
}

Errors

HTTPCodeWhen
400invalid_parameterMalformed chat id.
404not_foundNo message of this chat is visible to the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_linkedin_conversation

POST/v1/linkedin/conversations/:chatId/messagescampaigns:controlnewconfirmSend a LinkedIn message

Sends a REAL LinkedIn message in the chat for 5 credits; the AI stops messaging the contact (human takeover) and the CRM lead moves to Manager Dialog. Requires a connected account owned by the key's user. dry_run shows recipient, text, cost and whether the balance suffices.

Credits: 5 credits · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
chatIdpathstringyes
textbodystringyes≤8000
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/linkedin/conversations/$CHAT_ID/messages" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true, "text": "Great — I'\''ll send an invite for Thursday 3pm."}'

Response

json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "chat_id": "Xk3pQ9vR2sT8uY1w",
    "text": "Great — I'll send an invite for Thursday 3pm.",
    "sender_type": "self",
    "sent_by": "human",
    "message_timestamp": "2026-10-26T11:30:00.000Z"
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundChat not visible to the workspace.
409no_own_linkedin_accountThe key user has no connected account.
402insufficient_creditsBalance below 5 credits.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_send_linkedin_message, send_linkedin_message

Message previews 3

POST/v1/agents/:id/preview/emailagents:readupdatedPreview an agent email

Renders a sample email from the agent. Previously the request body was ignored; now mode, prompt, tone, customInstructions, variant, followUp {index, delayDays, previous} and sample render exactly like a campaign would. Without these fields the behaviour is unchanged. No sending, no credits.

Parameters

NameInTypeReq.Description
idpathuuidyesAgent id.
modebodystringone of: ai, fixed
promptbodystring
tonebodystringone of: formal, casual, friendly
customInstructionsbodystring
variantbodyobject
followUpbodyobject
samplebodyobject

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/preview/email" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Two sentences, mention their hiring", "tone": "casual"}'

Response

json
{
  "data": {
    "mode": "ai",
    "subject": "Hiring 3 SDRs at Acme?",
    "body": "Hi Alex, saw Acme is hiring three SDRs..."
  }
}

Errors

HTTPCodeWhen
404not_foundAgent not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_agent_email

POST/v1/agents/:id/preview/linkedin-followupagents:readconfirmPreview the LinkedIn follow-up message

Render a sample first follow-up (after the connection is accepted). No sending, no cost.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/preview/linkedin-followup" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_linkedin_followup

POST/v1/agents/:id/preview/linkedin-inviteagents:readconfirmPreview the LinkedIn invite text

Render a sample LinkedIn connection request from this agent. No sending, no cost.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/agents/$AGENT_ID/preview/linkedin-invite" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_linkedin_invite

Agent analytics & credits 7

GET/v1/agents/credits-spentagents:readWhole-workspace agent credit breakdown

Credits spent by every agent of the workspace, for the agents dashboard. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/credits-spent" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

GET/v1/agents/dashboard/analyticsagents:readDashboard summary

Older overview numbers of all agents (superseded by get_analytics_agents). Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/dashboard/analytics" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agents_dashboard

GET/v1/agents/with-statsagents:readList agents with connection + credit stats

Same as list_agents but includes per-agent stats (connections, credits spent, status).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/with-stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_agents_with_stats

GET/v1/agents/:id/connections/:cid/analyticsagents:readnewWidget channel analytics

The channel page numbers for a date range: widget loaded / activated / chats started / voice started, and estimated credits (text messages, voice minutes).

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesAgent id
cidpathstring (uuid)yesChannel (connection) id
date_fromquerydate
date_toquerydate

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/analytics" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "events": {
      "widgetLoaded": 1520,
      "widgetActivated": 210,
      "chatStarted": 88,
      "voiceStarted": 19
    },
    "credits": {
      "estimatedCredits": 412,
      "breakdown": {
        "textMessages": 640,
        "voiceMinutes": 21,
        "textCredits": 118,
        "voiceCredits": 294
      }
    }
  }
}

Errors

HTTPCodeWhen
404not_foundChannel does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_connection_analytics

GET/v1/agents/:id/connections/:cid/statsagents:readPer-connection stats

Conversations, messages, unique visitors, last activity of one channel. For widgets prefer get_connection_analytics (fresher). Read-only.

Parameters

NameInTypeReq.Description
idpathstringyesAgent id (UUID), from list_agents
cidpathstringyesChannel (connection) id (UUID), from list_agent_connections

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/connections/$CONNECTION_ID/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_agent_connection_stats

GET/v1/agents/:id/credits-spentagents:readCredits this agent has spent

Credits this agent has spent, as shown on the agent card. Read-only.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/credits-spent" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

GET/v1/agents/:id/credits-spent/connectionsagents:readPer-connection credit breakdown

Credits this agent has spent, broken down per channel (connection). Read-only.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/agents/$AGENT_ID/credits-spent/connections" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

Meetings API 6

The meetings calendar: meetings booked by your agents, transcripts, cancelling and removing the agent from a meeting.

Meetings calendar 6

GET/v1/meetingsagents:readnewMeetings calendar

Google Meet meetings of all your agents in a range (default next 7 days, max 62), the calendar page. Cancelled meetings are excluded. Meetings are per user: a teammate's key sees the teammate's meetings.

Parameters

NameInTypeReq.Description
startquerydatetimeISO
endquerydatetimeISO
agent_idquerystring (uuid)Filter

Request

curl
curl -s "https://api.vendo-ai.com/v1/meetings" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "meetings": [
      {
        "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
        "title": "Acme × Globex intro",
        "description": null,
        "start_time": "2026-10-28T14:00:00.000Z",
        "end_time": "2026-10-28T14:30:00.000Z",
        "meet_link": "https://meet.google.com/abc-defg-hij",
        "organizer_email": "ceo@globex.com",
        "organizer_name": "Jane Doe",
        "attendees": [
          "ceo@globex.com",
          "agent-3f6b2c1e@meet.vendo-ai.com"
        ],
        "status": "scheduled",
        "connection_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
        "has_transcript": false,
        "agent_in_call": false
      }
    ],
    "start": "2026-10-26T00:00:00.000Z",
    "end": "2026-11-02T00:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestBad range

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_meetings

GET/v1/meetings/agentsagents:readnewAgents with Google Meet

Agents that have an active Google Meet connection (calendar sidebar filter).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/meetings/agents" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
      "name": "Anna",
      "avatar_url": "avatar-2",
      "meeting_mode": "voice_only"
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_meeting_agents

GET/v1/meetings/:meetingIdagents:readnewMeeting details

One meeting with transcript, summary, action items, join/leave times and duration (the meeting page).

Parameters

NameInTypeReq.Description
meetingIdpathstring (uuid)yes

Request

curl
curl -s "https://api.vendo-ai.com/v1/meetings/$MEETING_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
    "title": "Acme × Globex intro",
    "description": null,
    "start_time": "2026-10-28T14:00:00.000Z",
    "end_time": "2026-10-28T14:30:00.000Z",
    "meet_link": "https://meet.google.com/abc-defg-hij",
    "organizer_email": "ceo@globex.com",
    "organizer_name": "Jane Doe",
    "attendees": [
      "ceo@globex.com",
      "agent-3f6b2c1e@meet.vendo-ai.com"
    ],
    "status": "scheduled",
    "connection_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "agent_id": "3f6b2c1e-8a4d-4f0b-9c2e-7d1a5b6e9f10",
    "has_transcript": false,
    "agent_in_call": false,
    "transcript": null,
    "summary": null,
    "action_items": null,
    "joined_at": null,
    "left_at": null,
    "duration_minutes": null
  }
}

Errors

HTTPCodeWhen
404not_foundMeeting does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_meeting

POST/v1/meetings/:meetingId/cancelagents:writenewconfirmCancel a meeting for the agent

The agent will not join this meeting (the calendar event itself is unchanged). Only scheduled meetings that have not started. No undo → dry_run / confirm.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
meetingIdpathstring (uuid)yes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/meetings/$MEETING_ID/cancel" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundMeeting does not exist or is not in your workspace
400invalid_requestNot scheduled or already started

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_cancel_meeting, cancel_meeting

POST/v1/meetings/:meetingId/remove-agentagents:writenewconfirmRemove the agent from a live call

The agent's bot leaves the live call now. Only for in-progress meetings with the agent in the call. dry_run / confirm.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
meetingIdpathstring (uuid)yes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/meetings/$MEETING_ID/remove-agent" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "success": true,
    "message": "Agent removed from meeting"
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundMeeting does not exist or is not in your workspace
400invalid_requestMeeting not in progress / agent not in the call

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_remove_agent_from_meeting, remove_agent_from_meeting

GET/v1/meetings/:meetingId/transcriptagents:readnewMeeting transcript

Transcript, summary and action items; source = database, recall_ai or none.

Parameters

NameInTypeReq.Description
meetingIdpathstring (uuid)yes

Request

curl
curl -s "https://api.vendo-ai.com/v1/meetings/$MEETING_ID/transcript" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5d1e8f2a-3b4c-4d6e-8f9a-0b1c2d3e4f56",
    "title": "Acme × Globex intro",
    "messages": [],
    "summary": null,
    "action_items": [],
    "source": "none"
  }
}

Errors

HTTPCodeWhen
404not_foundMeeting does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_meeting_transcript

Vendo SDR API 69

Signal-based prospecting: the New SDR wizard (company + website → ICP research), searches, the 24 signal hunters, contacts with ICP QA, outreach and search analytics.

The 24 signal hunters (ids for enabled_sources)

A new search enables all 24 hunters when enabled_sources is omitted (same as the web wizard). Hunters are not billed per run; every usable contact costs 5 credits. opt-in hunters are never switched on by platform defaults; paid engine means Vendo pays a search/AI provider; LinkedIn budget hunters share your accounts' daily LinkedIn budget. Live catalogue: GET /v1/sdr/hunters.

IdNameGroupWhereHowFlags
linkedin_jobsLinkedIn hiring signalslinkedinLinkedIn job postsWatches companies hiring for the roles that signal a need for your product, then finds the decision-makers at those companies.LinkedIn budget
linkedin_companyLinkedIn company announcementslinkedinLinkedIn company pagesReads company announcements (launches, new offices, expansion, partnerships) and treats them as timing signals.LinkedIn budget
linkedin_activityLinkedIn buying-intent postslinkedinLinkedIn posts and commentsRuns your buyer-intent queries against public posts; a person writing about the pain you solve becomes a lead.LinkedIn budget
ai_web_hunterAI web hunterwebThe open web (engine A)Searches the open web for dated company news that match your ICP (funding rounds, launches, expansion, new leaders) and keeps only first-party events.opt-in, paid engine, daily cap
social_signal_hunterSocial buying intentwebLinkedIn, X, Reddit, Facebook groups, YouTube, Quora, Threads, Telegram, Hacker News, Stack OverflowReads public posts for first-person needs: tool requests, operational pains, switching, hiring and launches. The company named in the post becomes a signal; a LinkedIn author becomes a contact.opt-in, paid engine, daily cap
crunchbase_rssFunding newswebFunding news: 35+ startup and VC media feeds by region plus the news index in your countries' languagesPicks up companies that just raised a round, explodes weekly deal digests into every company listed, and saves the founder/CEO named in the announcement as a contact.
hn_hiringHacker News hiringjobsHacker News: Who is hiring, Seeking freelancer, Show HN / Launch HNReads the monthly hiring and freelancer threads in full and every product launch of the last 7 days; the founder or hiring lead becomes a contact.
g2_reviewsCompetitor switchingwebCompetitor mentions: LinkedIn posts, X, Reddit, review sitesPeople saying in public that their company is leaving, evaluating, unhappy with or renewing a competitor product; the author becomes the contact. Competitors come from your ICP profile, research and competitor_pages.daily cap
tech_stackTech-stack changeswebATS job posts, competitor customer stories, website tool signaturesFinds companies that run a competitor or category tool: job posts that require the tool, new customer stories on the competitor's site, and tool signatures on the websites of companies already in your search.daily cap
connected_peopleDecision-maker matchinginternalInside every signalFor each company signal it finds the right decision-makers (names, titles, LinkedIn profiles), using decision_roles first.internal step, LinkedIn budget
email_enricherEmail discoveryinternalEmail discovery and verificationFinds and verifies a business email for every contact (patterns, MX/SMTP checks) and drops contacts outside target_cities.internal step
linkedin_engagementLinkedIn post engagementlinkedinReactions and comments on LinkedInCollects the people who liked or commented on buying-intent posts in your niche.opt-in, LinkedIn budget
linkedin_job_changeLinkedIn new-in-rolelinkedinLinkedIn career updatesFinds people who started an ICP role in the last 90 days; new leaders re-evaluate vendors in their first months.opt-in, LinkedIn budget
linkedin_competitor_followersCompetitor engagementlinkedinReactions to competitor postsReads the recent posts of your competitors' LinkedIn pages and picks the decision-makers who react to them.opt-in, LinkedIn budget
cross_poolShared signalsinternalSignals found for other searchesReuses fresh matching signals discovered for other searches in Vendo; free and instant.always on, not runnable
people_poolContact poolinternalThe Vendo People database (700K+)Matches your ICP against contacts Vendo already knows; the fastest first contacts on a new search.always on, not runnable
github_issuesGitHub buying intentwebGitHub issues and pull requestsDevelopers at named organisations describing a need in the open.opt-in
adzuna_jobsJob boards (19 countries)jobsJob boards in 19 countries (Adzuna)Hiring intent outside LinkedIn: local job boards across Europe, the Americas and APAC.opt-in
corporate_registriesExecutive appointmentsdataCorporate registries (SEC 8-K Item 5.02 …)Newly appointed executives from official filings; a new CxO is a strategic window for outreach.opt-in
web_news_radarWeb news radarwebThe open web (engine B)The same ICP-driven news hunt on an independent search engine. Idle while the engine has no provider key.opt-in, paid engine
web_pulse_hunterWeb pulse hunterwebThe open web (engine C)A third independent news engine on the same templates. Idle while the engine has no provider key.opt-in, paid engine
google_placesLocal businesses (Google Places)dataGoogle Maps business listingsFor local-business ICPs: finds businesses by category and geography (salons, clinics, agencies, dealers) with phone and website.opt-in, paid engine
web_profileWebsite profilewebThe company's own websiteReads the website of every company already in the search (agency, ad pixels, booking, stale blog, platform, open roles) for personalisation; named decision-makers on the team page become contacts.opt-in
local_listingsLocal businesses (listings)dataLocal business listings databaseFor local-business ICPs: businesses by category and city with phone, website, rating and claimed status, including businesses without a website. Idle while the provider has no credentials.opt-in, paid engine

Overview & live signals 4

GET/v1/sdr/company-heatanalytics:readupdatedHot companies (one search or workspace-wide)

Companies ranked by live signal heat. With search_id: that search with total and offset paging (limit up to 500). Without: merged across the workspace's searches (first 500 rows: offset + limit ≤ 500), it used to return an arbitrary first search.

Parameters

NameInTypeReq.Description
search_idqueryuuidOne search
limitqueryinteger1-500, default 50
offsetqueryintegerPaging offset

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/company-heat" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "count": 1,
    "total": 23,
    "offset": 0,
    "companies": [
      {
        "company_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
        "company_name": "Northgate Dental",
        "company_domain": "northgatedental.co.uk",
        "heat": 82,
        "icp_fit": "fit",
        "signal_count": 3,
        "signal_types": [
          "hiring_role",
          "news_mention"
        ],
        "contact_status": "found",
        "contacts_found": 2
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_company_heat

GET/v1/sdr/learning-insightsanalytics:readupdatedSignal Learning insights

Learned weight per signal type and conversion from real outreach outcomes. With search_id: that search. Without: one entry per search of the workspace (it used to return an arbitrary first search).

Parameters

NameInTypeReq.Description
search_idqueryuuidOne search

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/learning-insights" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "weights": [
      {
        "signal_type": "hiring_role",
        "weight": 1.32,
        "samples": 140
      }
    ],
    "conversion": [
      {
        "signal_type": "hiring_role",
        "sent": 140,
        "replied": 6,
        "meetings": 2
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_learning_insights

GET/v1/sdr/overviewanalytics:readnewAll SDR processes with health numbers

The /vendo-sdr list page: every search of the workspace with signals (7 days, previous 7, 30 days), contacts (total, with email, new in 7 days), hot companies, hunters enabled/running/cooling/paused, outreach delivery and a daily series, plus workspace totals. Read-only.

Parameters

NameInTypeReq.Description
daysqueryintegerDaily series length 7-60, default 14

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/overview" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (46 lines)
json
{
  "data": {
    "workspace_id": "0d9e8f7a-6b5c-4d3e-2f1a-0b9c8d7e6f5a",
    "days": [
      "2026-10-13",
      "…",
      "2026-10-26"
    ],
    "totals": {
      "searches": 2,
      "active": 2,
      "signals_7d": 184,
      "signals_prev7d": 151,
      "signals_30d": 702,
      "contacts_total": 431,
      "contacts_email": 318,
      "contacts_new7d": 57,
      "hot_companies": 23,
      "hunters_running": 31,
      "hunters_enabled": 46,
      "daily": [
        12,
        15,
        9
      ]
    },
    "searches": [
      {
        "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
        "name": "UK dental clinics — buyers",
        "status": "active",
        "signals_7d": 120,
        "contacts_total": 260,
        "hunters_enabled": 24,
        "hunters_running": 18,
        "hunters_cooling": 2,
        "hunters_paused": 0,
        "daily": [
          8,
          10,
          6
        ]
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_overview

GET/v1/sdr/recentanalytics:readupdatedRecent signals (one search or workspace-wide)

Latest signals. With search_id: that search. Without: merged across the workspace's searches (newest first, each row tagged with search_id/search_name), it used to return an arbitrary first search.

Parameters

NameInTypeReq.Description
search_idqueryuuidOne search
limitqueryinteger1-100, default 20

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/recent" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": null,
    "searches_scanned": 2,
    "signals": [
      {
        "id": "3a4b5c6d-7e8f-4091-a2b3-c4d5e6f7a8b9",
        "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
        "search_name": "UK dental clinics — buyers",
        "source": "linkedin_jobs",
        "signal_type": "hiring_role",
        "company_name": "Northgate Dental",
        "topic": "Practice Manager",
        "detected_at": "2026-10-26T10:58:00Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_recent_signals

Wizard & ICP research 9

GET/v1/sdr/agents/:id/profileaudiences:readGet the agent's current signal profile

Read the agent's current signal profile (ICP, offer summary, hunter weights). Returns { exists: false } when nothing's generated yet.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/agents/$AGENT_ID/profile" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_agent_signal_profile

POST/v1/sdr/agents/:id/profile/generateaudiences:write(Re-)generate the signal profile (Claude call)

Run Claude on the agent's knowledge to (re)generate its signal profile. Persists onto the agent so later Signal Searches reuse it.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/agents/$AGENT_ID/profile/generate" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_generate_agent_signal_profile

GET/v1/sdr/geo/citiesaudiences:readnewFind cities for target_cities

The Regions step city picker. Without q it returns the most populous cities per country/state (default 12, max 30 each); with q (2+ Latin letters) it searches names inside those countries (default 20, max 50). Use the objects as target_cities. Static GeoNames data (places of 5 000+ people), no side effects.

Parameters

NameInTypeReq.Description
countriesquerystringyesComma-separated ISO-2 or CC:SUB codes, max 40 (sub-national filter applies to US states only)
qquerystringName search, 2-80 characters
limitqueryintegerPer country without q (1-30), total with q (1-50)

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/geo/cities?countries=$COUNTRIES" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "cities": [
      {
        "country": "GB",
        "name": "London",
        "admin1": "England",
        "admin1_code": "ENG",
        "population": 8961989,
        "lat": 51.50853,
        "lng": -0.12574
      },
      {
        "country": "GB",
        "name": "Manchester",
        "admin1": "England",
        "admin1_code": "ENG",
        "population": 395515,
        "lat": 53.48095,
        "lng": -2.23743
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_find_cities

GET/v1/sdr/researchaudiences:readnewSetup state and latest research of a company profile

Poll this after POST /v1/sdr/research while the company profile is being built (setup.status running → done / error), and to read the latest ICP research of a company profile. setup is null when no background build ran in the last 24 h. Read-only.

Parameters

NameInTypeReq.Description
agent_idqueryuuidyesCompany profile id returned by POST /v1/sdr/research

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/research?agent_id=$AGENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (58 lines)
json
{
  "data": {
    "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
    "setup": {
      "status": "done",
      "phase": "research",
      "message": "Company profile ready; ICP research started.",
      "job_id": "e7d41b2a-0c58-4a9e-b6f3-91a2d4c8e5f7"
    },
    "job": {
      "id": "e7d41b2a-0c58-4a9e-b6f3-91a2d4c8e5f7",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "company_name": "Brightsmile Software",
      "website": "https://brightsmile.io",
      "status": "running",
      "current_stage": "web",
      "stages": [
        {
          "key": "profile",
          "status": "done"
        },
        {
          "key": "web",
          "status": "running"
        }
      ],
      "result": null,
      "error": null,
      "instant_audience_summary": null
    },
    "stages": [
      {
        "key": "profile",
        "title": "Reading your website"
      },
      {
        "key": "web",
        "title": "Researching your market on the web"
      },
      {
        "key": "icp",
        "title": "Building your ideal customer profile"
      },
      {
        "key": "qa",
        "title": "Checking the profile for consistency"
      },
      {
        "key": "save",
        "title": "Preparing your hunting plan"
      },
      {
        "key": "audience",
        "title": "Finding your first buyers in our database"
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_research

POST/v1/sdr/researchaudiences:writenewconfirmStart the New SDR wizard for a company website

Company + website step of /vendo-sdr/new, run server-side. Reuses the workspace's company profile for the same domain, or builds a hidden one exactly like the web app (create profile, website deep scan 15 credits, save scan data, outreach playbook 1 credit, activate) in the background; then runs the ICP research (or reuses one under 24 h old unless force). Hidden profiles never appear in /v1/agents and do not count toward the agent limit. dry_run shows reuse and cost; confirm starts. At most 5 new company profiles per API key per hour.

Credits: 16 credits when a new company profile is built (deep scan 15 + playbook 1); 0 when reused · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
company_namebodystringyesCompany name
websitebodystringyesDomain or URL, e.g. acme.com
forcebodybooleanRun a fresh research even if one under 24 h exists
dry_runbodybooleanPreview only
confirmbodybooleanExecute

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/research" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_name": "Brightsmile Software", "website": "brightsmile.io", "confirm": true}'

Response

json
{
  "data": {
    "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
    "reused_company_profile": false,
    "setup": {
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "company_name": "Brightsmile Software",
      "website": "https://brightsmile.io",
      "domain": "brightsmile.io",
      "status": "running",
      "phase": "deep_scan",
      "message": "Reading your website…",
      "error": null,
      "job_id": null,
      "started_at": "2026-10-26T10:02:11.000Z",
      "finished_at": null,
      "phases": [
        "company",
        "deep_scan",
        "save_scan",
        "script",
        "activate",
        "research"
      ]
    },
    "job": null,
    "next": "GET /v1/sdr/research?agent_id=4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44 every 5-10 s until setup.status is done (the research job then appears) or error."
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400confirmation_requiredNeither dry_run nor confirm was sent
400forbidden_fieldThe body carries a field that cannot be set here
400invalid_requestInsufficient credits (agent creation needs ≥1, deep scan 15)
429rate_limitedMore than 5 new company profiles from this key in an hour

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_start_research, sdr_start_research

GET/v1/sdr/research/:idaudiences:readnewPoll one ICP research job

Replaces the research SSE stream: poll every 2-5 s until status is done or error. When done, result holds the company dossier and the editable profile (search_name, offer_summary, icp_summary, buyer_type, industry_keywords, decision_roles, company_sizes, target_regions, region_reasoning, languages, pains, buying_triggers, relevant_topics, signal_weights, exclude_keywords, linkedin_search_queries, competitor_pages, hunter_plan, reasoning), QA notes, pages read and web sources. The research itself is not billed in credits.

Parameters

NameInTypeReq.Description
idpathuuidyesResearch job id

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/research/$RESEARCH_JOB_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (85 lines)
json
{
  "data": {
    "job": {
      "id": "e7d41b2a-0c58-4a9e-b6f3-91a2d4c8e5f7",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "status": "done",
      "current_stage": null,
      "stages": [
        {
          "key": "profile",
          "status": "done",
          "started_at": "2026-10-26T10:05:01Z",
          "finished_at": "2026-10-26T10:05:14Z"
        }
      ],
      "events": [
        {
          "ts": "2026-10-26T10:05:20Z",
          "type": "log",
          "stage": "web",
          "message": "Reading competitor pricing pages"
        }
      ],
      "result": {
        "company": {
          "name": "Brightsmile Software",
          "hq_country": "GB"
        },
        "profile": {
          "search_name": "UK dental clinics — buyers",
          "offer_summary": "Online booking and recall automation for dental clinics.",
          "icp_summary": "Independent dental clinics with 2–10 chairs.",
          "decision_roles": [
            "Practice Owner",
            "Practice Manager"
          ],
          "company_sizes": [
            "11–50"
          ],
          "target_regions": [
            "GB"
          ],
          "languages": [
            "English"
          ],
          "industry_keywords": [
            "dental clinic"
          ],
          "competitor_pages": [
            "https://www.linkedin.com/company/dentally/"
          ],
          "hunter_plan": [
            {
              "source": "linkedin_jobs",
              "why": "Clinics hiring practice managers"
            }
          ]
        },
        "qa": {
          "ok": true,
          "issues": [],
          "applied": []
        },
        "pages_read": [
          {
            "url": "https://brightsmile.io/",
            "title": "Brightsmile"
          }
        ],
        "web_sources": [],
        "generated_at": "2026-10-26T10:07:40Z"
      },
      "error": null,
      "instant_audience_summary": {
        "total": 1840,
        "shown": 20,
        "with_email": 14,
        "with_photo": 17,
        "generated_at": "2026-10-26T10:08:30Z",
        "materialized": null
      }
    },
    "stages": []
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_research

GET/v1/sdr/research/:id/audienceaudiences:readnewFirst buyers found by a research

The review step's "buyers already found" cards: up to ~20 people from the Vendo People database matching the researched ICP, with company, position, country, LinkedIn, email (shown in full, as in the UI) and an AI fit explanation. Cached on the job; refresh=1 rebuilds it (up to ~60 s). Read-only, no credits.

Parameters

NameInTypeReq.Description
idpathuuidyesResearch job id
refreshquery0|1Rebuild the audience

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/research/$RESEARCH_JOB_ID/audience" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (49 lines)
json
{
  "data": {
    "people": [
      {
        "id": "pp_88123",
        "name": "Sarah Collins",
        "position": "Practice Manager",
        "company": "Northgate Dental",
        "country": "United Kingdom",
        "city": "Leeds",
        "industry": "Dental",
        "avatar_url": null,
        "linkedin_url": "https://www.linkedin.com/in/sarah-collins-dental",
        "has_email": true,
        "email": "sarah@northgatedental.co.uk",
        "website": "https://northgatedental.co.uk",
        "fit": {
          "role": "Practice Manager",
          "industry": "dental",
          "region": "GB"
        },
        "ai": {
          "company_summary": "Three-chair family practice.",
          "fit_reason": "Runs operations and booking at an independent clinic.",
          "fit_score": 86,
          "matched": {
            "role": true,
            "industry": "dental",
            "region": true
          }
        }
      }
    ],
    "total": 1840,
    "with_photo": 17,
    "with_email": 14,
    "countries": [
      "United Kingdom"
    ],
    "positions": [
      "Practice Manager"
    ],
    "industries": [
      "Dental"
    ],
    "took_ms": 4210,
    "generated_at": "2026-10-26T10:08:30Z"
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_research_audience

POST/v1/sdr/research/:id/materializeaudiences:writenewCopy a research's first buyers into a search

Copies the instant audience into the search as contacts in the background (idempotent, filtered by the search's regions and cities). POST /v1/sdr/searches with research_job_id does this automatically after activation. Contacts are billed like any other contact (5 credits) when used.

Parameters

NameInTypeReq.Description
idpathuuidyesResearch job id
search_idbodyuuidyesTarget search of this workspace

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/research/$RESEARCH_JOB_ID/materialize" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10"}'

Response

json
{
  "data": {
    "ok": true,
    "started": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace
400forbidden_fieldThe body carries a field that cannot be set here

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_materialize_research

POST/v1/sdr/searches/previewaudiences:writeupdatedPreview a search profile (no save)

Generates the signal-search profile (ICP, keywords, hunters) the app would propose for an agent, without saving anything. Runs one AI call that takes 30–90 s (no credits). Fixed (2026-10-26): the gateway waited only 30 s and then crashed with a raw 500; it now waits up to 180 s and a timeout answers 504 timeout.

Parameters

NameInTypeReq.Description
agent_idbodyuuidyesOne of your agents (GET /v1/agents)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/preview" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71"}'

Response

json
{
  "data": {
    "name": "UK dental clinics hiring front-desk staff",
    "icp": {
      "industries": [
        "Dental clinics"
      ],
      "locations": [
        "United Kingdom"
      ],
      "company_size": "2-50",
      "roles": [
        "Practice owner",
        "Practice manager"
      ]
    },
    "keywords": [
      "dental receptionist",
      "practice manager"
    ],
    "enabled_sources": [
      "linkedin_jobs",
      "google_places",
      "web_news_radar"
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestagent_id missing
404not_foundAgent does not exist or is not in your workspace
504timeoutThe AI call took longer than 180 s; retry

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_search

Searches 9

GET/v1/sdr/searchesaudiences:readList all signal searches

List active Signal Searches in this workspace. Optionally filter by agent.

Parameters

NameInTypeReq.Description
agent_idquerystring

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_list_searches

POST/v1/sdr/searchesaudiences:writeupdatedCreate an SDR search (Create & Start)

Creates a search the way the wizard's "Create & Start" does. With research_job_id every field defaults to the research profile (name, regions or HQ country, offer, ICP + decision-makers + languages + company size, keywords, LinkedIn queries, competitor pages, all 24 hunters), competitor pages are saved, the search is activated (start defaults to true) and the research's first buyers are copied in. Legacy: agent_id + name + target_regions; when no offer/ICP is given the profile is generated from the company profile. Contacts cost 5 credits each once hunting starts.

Credits: No charge to create; 5 credits per usable contact once hunting

Parameters

NameInTypeReq.Description
research_job_idbodyuuidFinished research job (recommended)
agent_idbodyuuidCompany profile id (legacy path; must match the research's if both are given)
namebodystringProcess name (max 200)
enabled_sourcesbodystring[]Hunters to run (GET /v1/sdr/hunters). At least one one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
target_regionsbodystring[]ISO-2 countries or US states as CC:SUB (e.g. GB, US:FL). At least one
target_citiesbodyobject[]Up to 100 {country, name, admin1?, admin1_code?, lat?, lng?} inside target_regions (GET /v1/sdr/geo/cities). Cities of removed countries are dropped; empty = whole countries
offer_summarybodystring"What you sell" (max 10 000)
icp_summarybodystring"Who buys". The "Decision-makers: …." sentence and the "Preferred company size: …" line are rebuilt from decision_roles and company_size, exactly as the web wizard does
decision_rolesbodystring[]Decision-maker job titles (max 20); also stored on their own field
company_sizebodystring|nulle.g. "11–50 employees"; null removes the size line
industry_keywordsbodystring[]Industries / keywords
exclude_keywordsbodystring[]Exclusions
linkedin_search_queriesbodyobject[]Up to 50 {query, location_hint?, rationale?}
competitor_pagesbodystring[]Competitor LinkedIn page URLs (Competitor Engagement / Competitor Switching hunters)
relevant_topicsbodyobject{signal_type: string[]}
signal_weightsbodyobject{signal_type: number 0-10}
languagesbodystring[]Outreach languages, written into icp_summary
generation_reasoningbodystringFree-text reasoning kept with the profile
generation_modelbodystringLabel of what generated the profile
startbodybooleanActivate hunters right away (default true)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "research_job_id": "e7d41b2a-0c58-4a9e-b6f3-91a2d4c8e5f7",
  "target_regions": [
    "GB"
  ],
  "target_cities": [
    {
      "country": "GB",
      "name": "Manchester"
    }
  ],
  "company_size": "11–50 employees"
}'

Response

Show the full response (86 lines)
json
{
  "data": {
    "search": {
      "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "name": "UK dental clinics — buyers",
      "status": "active",
      "enabled_sources": [
        "linkedin_jobs",
        "linkedin_company",
        "linkedin_activity",
        "ai_web_hunter",
        "social_signal_hunter",
        "crunchbase_rss",
        "hn_hiring",
        "g2_reviews",
        "tech_stack",
        "connected_people",
        "email_enricher",
        "linkedin_engagement",
        "linkedin_job_change",
        "linkedin_competitor_followers",
        "cross_pool",
        "people_pool",
        "github_issues",
        "adzuna_jobs",
        "corporate_registries",
        "web_news_radar",
        "web_pulse_hunter",
        "google_places",
        "web_profile",
        "local_listings"
      ],
      "offer_summary": "Online booking and recall automation for dental clinics.",
      "icp_summary": "Independent dental clinics with 2–10 chairs planning to grow patient volume. Decision-makers: Practice Owner, Practice Manager.\nPreferred company size: 11–50 employees.",
      "decision_roles": [
        "Practice Owner",
        "Practice Manager"
      ],
      "industry_keywords": [
        "dental clinic",
        "dentist"
      ],
      "exclude_keywords": [
        "dental lab"
      ],
      "linkedin_search_queries": [
        {
          "query": "practice manager dental",
          "location_hint": "United Kingdom"
        }
      ],
      "competitor_pages": [
        "https://www.linkedin.com/company/dentally/"
      ],
      "target_regions": [
        "GB"
      ],
      "target_cities": [
        {
          "country": "GB",
          "name": "Manchester",
          "admin1": "England",
          "lat": 53.48,
          "lng": -2.24
        }
      ],
      "hot_threshold": 70,
      "human_edited": true,
      "created_at": "2026-10-26T09:12:44.000Z"
    },
    "activated": true,
    "started": [
      "linkedin_jobs",
      "linkedin_company",
      "connected_people",
      "email_enricher",
      "people_pool"
    ],
    "instant_audience": {
      "ok": true,
      "started": true
    },
    "warnings": []
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace
409research_not_readyThe research job is not done yet
409conflictA live search with the same name, or an identical one created minutes ago, exists for this company profile

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_create_search

GET/v1/sdr/searches/:idaudiences:readGet search details

Fetch one Signal Search by id, including its profile, hunters, and contact counts.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_search

PATCH/v1/sdr/searches/:idaudiences:writeupdatedEdit an SDR search (wizard Save changes)

Explicit allow-list of the editable profile fields; status, agent_id, custom-planning and internal flags are rejected with forbidden_field. Hunter ids are validated against the catalogue. icp_summary, decision_roles and company_size are recombined exactly as the edit wizard does. Status is not touched: an active search keeps hunting with the new profile.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
namebodystringProcess name (max 200)
enabled_sourcesbodystring[]Hunters to run (GET /v1/sdr/hunters). At least one one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
target_regionsbodystring[]ISO-2 countries or US states as CC:SUB (e.g. GB, US:FL). At least one
target_citiesbodyobject[]Up to 100 {country, name, admin1?, admin1_code?, lat?, lng?} inside target_regions (GET /v1/sdr/geo/cities). Cities of removed countries are dropped; empty = whole countries
offer_summarybodystring"What you sell" (max 10 000)
icp_summarybodystring"Who buys". The "Decision-makers: …." sentence and the "Preferred company size: …" line are rebuilt from decision_roles and company_size, exactly as the web wizard does
decision_rolesbodystring[]Decision-maker job titles (max 20); also stored on their own field
company_sizebodystring|nulle.g. "11–50 employees"; null removes the size line
industry_keywordsbodystring[]Industries / keywords
exclude_keywordsbodystring[]Exclusions
linkedin_search_queriesbodyobject[]Up to 50 {query, location_hint?, rationale?}
competitor_pagesbodystring[]Competitor LinkedIn page URLs (Competitor Engagement / Competitor Switching hunters)
relevant_topicsbodyobject{signal_type: string[]}
signal_weightsbodyobject{signal_type: number 0-10}
hot_thresholdbodyintegerHot company threshold 0-100 (default 70)

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "decision_roles": [
    "Practice Owner",
    "Clinical Director"
  ],
  "target_cities": [],
  "enabled_sources": [
    "linkedin_jobs",
    "connected_people",
    "email_enricher",
    "people_pool",
    "cross_pool"
  ]
}'

Response

Show the full response (74 lines)
json
{
  "data": {
    "ok": true,
    "search": {
      "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "name": "UK dental clinics — buyers",
      "status": "active",
      "enabled_sources": [
        "linkedin_jobs",
        "linkedin_company",
        "linkedin_activity",
        "ai_web_hunter",
        "social_signal_hunter",
        "crunchbase_rss",
        "hn_hiring",
        "g2_reviews",
        "tech_stack",
        "connected_people",
        "email_enricher",
        "linkedin_engagement",
        "linkedin_job_change",
        "linkedin_competitor_followers",
        "cross_pool",
        "people_pool",
        "github_issues",
        "adzuna_jobs",
        "corporate_registries",
        "web_news_radar",
        "web_pulse_hunter",
        "google_places",
        "web_profile",
        "local_listings"
      ],
      "offer_summary": "Online booking and recall automation for dental clinics.",
      "icp_summary": "Independent dental clinics with 2–10 chairs planning to grow patient volume. Decision-makers: Practice Owner, Practice Manager.\nPreferred company size: 11–50 employees.",
      "decision_roles": [
        "Practice Owner",
        "Practice Manager"
      ],
      "industry_keywords": [
        "dental clinic",
        "dentist"
      ],
      "exclude_keywords": [
        "dental lab"
      ],
      "linkedin_search_queries": [
        {
          "query": "practice manager dental",
          "location_hint": "United Kingdom"
        }
      ],
      "competitor_pages": [
        "https://www.linkedin.com/company/dentally/"
      ],
      "target_regions": [
        "GB"
      ],
      "target_cities": [
        {
          "country": "GB",
          "name": "Manchester",
          "admin1": "England",
          "lat": 53.48,
          "lng": -2.24
        }
      ],
      "hot_threshold": 70,
      "human_edited": true,
      "created_at": "2026-10-26T09:12:44.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_update_search

DELETE/v1/sdr/searches/:idaudiences:writeupdatedconfirmDelete an SDR process and all its data

Irreversible: hunters stop, the outreach campaign is stopped and deleted with its recipients and events, and every signal, contact, hunter state, ICP-QA run, outcome and learning weight of the search is removed. dry_run now reports reachable contacts, signals of the last 90 days and the outreach campaign status. Exported audiences are kept; no refund.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
dry_runbodybooleanPreview
confirmbodybooleanExecute

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Delete the SDR process \"UK dental clinics — buyers\" and ALL its data, irreversibly: …",
    "search": {
      "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "name": "UK dental clinics — buyers",
      "status": "active"
    },
    "estimated": {
      "contacts_reachable": 260,
      "signals_last_90_days": 702,
      "outreach_campaign": {
        "status": "active",
        "channels": [
          "email",
          "linkedin"
        ]
      }
    },
    "reversible": false,
    "note": "Credits already spent on contacts are not refunded. Custom audiences you exported earlier are kept."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_delete_search, sdr_delete_search

POST/v1/sdr/searches/:id/activateaudiences:writeActivate a search

Set status=active and fire-start all enabled hunters. Reversible via sdr_pause_search.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/activate" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_activate_search

POST/v1/sdr/searches/:id/pauseaudiences:writeupdatedPause an SDR process

Pauses the whole process: every hunter stops AND outreach sending stops (email campaign paused, no LinkedIn sends). Data is kept. Route unchanged; documented semantics corrected (it used to say hunters only).

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/pause" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (75 lines)
json
{
  "data": {
    "ok": true,
    "search": {
      "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "name": "UK dental clinics — buyers",
      "status": "paused",
      "enabled_sources": [
        "linkedin_jobs",
        "linkedin_company",
        "linkedin_activity",
        "ai_web_hunter",
        "social_signal_hunter",
        "crunchbase_rss",
        "hn_hiring",
        "g2_reviews",
        "tech_stack",
        "connected_people",
        "email_enricher",
        "linkedin_engagement",
        "linkedin_job_change",
        "linkedin_competitor_followers",
        "cross_pool",
        "people_pool",
        "github_issues",
        "adzuna_jobs",
        "corporate_registries",
        "web_news_radar",
        "web_pulse_hunter",
        "google_places",
        "web_profile",
        "local_listings"
      ],
      "offer_summary": "Online booking and recall automation for dental clinics.",
      "icp_summary": "Independent dental clinics with 2–10 chairs planning to grow patient volume. Decision-makers: Practice Owner, Practice Manager.\nPreferred company size: 11–50 employees.",
      "decision_roles": [
        "Practice Owner",
        "Practice Manager"
      ],
      "industry_keywords": [
        "dental clinic",
        "dentist"
      ],
      "exclude_keywords": [
        "dental lab"
      ],
      "linkedin_search_queries": [
        {
          "query": "practice manager dental",
          "location_hint": "United Kingdom"
        }
      ],
      "competitor_pages": [
        "https://www.linkedin.com/company/dentally/"
      ],
      "target_regions": [
        "GB"
      ],
      "target_cities": [
        {
          "country": "GB",
          "name": "Manchester",
          "admin1": "England",
          "lat": 53.48,
          "lng": -2.24
        }
      ],
      "hot_threshold": 70,
      "human_edited": true,
      "created_at": "2026-10-26T09:12:44.000Z"
    },
    "outreachPaused": true
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_pause_search

POST/v1/sdr/searches/:id/regenerateaudiences:writeRegenerate search configuration

Re-run Claude on the agent's current knowledge to regenerate the entire ICP profile and hunter configuration. Useful after the agent's script changed significantly.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/regenerate" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_regenerate_search

POST/v1/sdr/searches/:id/resumeaudiences:writeupdatedResume an SDR process

Hunters restart and outreach that was paused together with the search resumes; outreach you paused separately stays paused. Route unchanged; documented semantics corrected.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/resume" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (79 lines)
json
{
  "data": {
    "ok": true,
    "search": {
      "id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "agent_id": "4c2e8a71-3b9d-4f60-8e15-7d0a9c6b2e44",
      "name": "UK dental clinics — buyers",
      "status": "active",
      "enabled_sources": [
        "linkedin_jobs",
        "linkedin_company",
        "linkedin_activity",
        "ai_web_hunter",
        "social_signal_hunter",
        "crunchbase_rss",
        "hn_hiring",
        "g2_reviews",
        "tech_stack",
        "connected_people",
        "email_enricher",
        "linkedin_engagement",
        "linkedin_job_change",
        "linkedin_competitor_followers",
        "cross_pool",
        "people_pool",
        "github_issues",
        "adzuna_jobs",
        "corporate_registries",
        "web_news_radar",
        "web_pulse_hunter",
        "google_places",
        "web_profile",
        "local_listings"
      ],
      "offer_summary": "Online booking and recall automation for dental clinics.",
      "icp_summary": "Independent dental clinics with 2–10 chairs planning to grow patient volume. Decision-makers: Practice Owner, Practice Manager.\nPreferred company size: 11–50 employees.",
      "decision_roles": [
        "Practice Owner",
        "Practice Manager"
      ],
      "industry_keywords": [
        "dental clinic",
        "dentist"
      ],
      "exclude_keywords": [
        "dental lab"
      ],
      "linkedin_search_queries": [
        {
          "query": "practice manager dental",
          "location_hint": "United Kingdom"
        }
      ],
      "competitor_pages": [
        "https://www.linkedin.com/company/dentally/"
      ],
      "target_regions": [
        "GB"
      ],
      "target_cities": [
        {
          "country": "GB",
          "name": "Manchester",
          "admin1": "England",
          "lat": 53.48,
          "lng": -2.24
        }
      ],
      "hot_threshold": 70,
      "human_edited": true,
      "created_at": "2026-10-26T09:12:44.000Z"
    },
    "started": [
      "linkedin_jobs",
      "connected_people"
    ],
    "outreachResumed": true
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_resume_search

Hunters 12

GET/v1/sdr/huntersaudiences:readupdatedHunter catalogue (24 sources)

Every hunter source a search can run, rebuilt from the current backend: UI name and dashboard station, where it looks and how, opt_in_only (never switched on by platform defaults), budget_throttled (daily cycle cap), paid_engine (Vendo pays a search/AI provider), uses_linkedin_budget, internal/always_on, runnable (Run button) and the search fields it reads. The web wizard and POST /v1/sdr/searches enable all 24 by default. Hunters are not billed per run; each usable contact costs 5 credits.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/hunters" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (36 lines)
json
{
  "data": {
    "hunters": [
      {
        "id": "linkedin_competitor_followers",
        "name": "Competitor engagement",
        "station": "Competitor Engagement Hunter",
        "group": "linkedin",
        "where": "Reactions to competitor posts",
        "how": "Reads the recent posts of your competitors' LinkedIn pages and picks the decision-makers who react to them.",
        "opt_in_only": true,
        "budget_throttled": false,
        "paid_engine": false,
        "uses_linkedin_budget": true,
        "internal": false,
        "always_on": false,
        "runnable": true,
        "inputs": [
          "competitor_pages",
          "target_regions"
        ],
        "requires": "competitor_pages (LinkedIn company page URLs) or competitors found by the ICP research"
      }
    ],
    "count": 24,
    "ui_default": "The web wizard enables all 24 hunters on a new search; POST /v1/sdr/searches does the same when enabled_sources is omitted.",
    "not_a_source": [
      {
        "id": "static_base",
        "name": "Static Base Hunter",
        "note": "Runs automatically every ~8 minutes on every active search."
      }
    ],
    "cost": "Hunters are not billed per run. Every usable contact a search delivers costs 5 credits (charged once per contact); contacts the ICP check marks Not a fit are free."
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_list_hunters

GET/v1/sdr/searches/:id/healthanalytics:readPer-search operator dashboard

Operator-level health view: states, signals/contacts produced, credits spent, recent events, learning insights for a single search.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/health" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_health

GET/v1/sdr/searches/:id/huntersaudiences:readnewHunter stations of a search

Every catalogue hunter for one search: enabled flag, live run state, signals in the last 30 days, a 7-day sparkline and the current values of the search fields it reads. static_base is reported separately. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/hunters" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (62 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "search_status": "active",
    "enabled_sources": [
      "linkedin_jobs",
      "connected_people"
    ],
    "hunters": [
      {
        "id": "linkedin_jobs",
        "name": "LinkedIn hiring signals",
        "station": "LinkedIn Hunter",
        "group": "linkedin",
        "enabled": true,
        "state": {
          "status": "running",
          "paused": false,
          "phase": "searching",
          "current_action": "practice manager dental"
        },
        "signals_30d": 182,
        "daily_7d": [
          4,
          6,
          3,
          8,
          5,
          7,
          2
        ],
        "paid_engine": false,
        "opt_in_only": false,
        "internal": false,
        "always_on": false,
        "runnable": true,
        "settings": {
          "linkedin_search_queries": [
            {
              "query": "practice manager dental"
            }
          ],
          "target_regions": [
            "GB"
          ]
        },
        "requires": null
      }
    ],
    "static_base": {
      "source": "static_base",
      "status": "running",
      "paused": false,
      "note": "Runs automatically every ~8 min"
    },
    "days": [
      "2026-10-20",
      "…"
    ],
    "note": "A hunter enabled on an active search starts within ~10 minutes (budget-throttled ones within ~30). A disabled hunter stops at its next cycle; pause it to stop at once."
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_search_hunters

PATCH/v1/sdr/searches/:id/huntersaudiences:writenewSwitch hunters on/off

The wizard's Hunters step for an existing search. Pass enable/disable lists or enabled_sources (full list, not both). On an active search added hunters start within ~10 minutes (budget-throttled ones ~30); removed hunters stop at their next cycle. At least one hunter must stay on.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
enablebodystring[]Sources to switch on one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
disablebodystring[]Sources to switch off one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
enabled_sourcesbodystring[]Full replacement list one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/hunters" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enable": ["linkedin_competitor_followers", "web_profile"], "disable": ["hn_hiring"]}'

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "enabled_sources": [
      "linkedin_jobs",
      "connected_people",
      "linkedin_competitor_followers",
      "web_profile"
    ],
    "added": [
      "linkedin_competitor_followers",
      "web_profile"
    ],
    "removed": [
      "hn_hiring"
    ],
    "note": "Added hunters start within ~10 minutes (budget-throttled ones within ~30); removed hunters stop at their next cycle."
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_set_hunters

GET/v1/sdr/searches/:id/hunters/:sourceaudiences:readnewHunter detail page

One hunter of a search, as on /vendo-sdr/search/:id/hunter/:source: catalogue entry, enabled, run state, signals in 30 days, signals and contacts per day, the contacts it found (including pending and phone-only ones) and its settings. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcepathstringyesHunter source id (GET /v1/sdr/hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
daysqueryintegerChart length 2-60, default 14
contacts_limitqueryinteger1-200, default 100

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/hunters/$SOURCE" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (55 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "hunter": {
      "id": "google_places",
      "name": "Local businesses (Google Places)"
    },
    "enabled": true,
    "state": {
      "status": "running"
    },
    "signals_30d": 64,
    "daily": {
      "days": [
        "2026-10-25",
        "2026-10-26"
      ],
      "signals": [
        5,
        3
      ],
      "contacts": [
        2,
        1
      ]
    },
    "contacts": {
      "items": [
        {
          "id": "1f8e2d3c-4b5a-4697-8a1b-2c3d4e5f6a7b",
          "full_name": "Amir Patel",
          "title": "Owner",
          "company_name": "Smile Studio Manchester",
          "email": "amir@smilestudio.co.uk",
          "icp_group": "fits"
        }
      ],
      "total": 41
    },
    "settings": {
      "industry_keywords": [
        "dental clinic"
      ],
      "target_regions": [
        "GB"
      ],
      "target_cities": [
        {
          "country": "GB",
          "name": "Manchester"
        }
      ]
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_hunter_detail

PATCH/v1/sdr/searches/:id/hunters/:sourceaudiences:writenewConfigure one hunter

Turns one hunter on/off and/or sets the search fields it reads, limited to its editable inputs: competitor_pages, target_cities, decision_roles, linkedin_search_queries, industry_keywords, exclude_keywords, relevant_topics. These settings are search-wide, so every hunter reading the same field sees the change on its next cycle. decision_roles also rebuilds the ICP sentence, as in the wizard.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcepathstringyesHunter source id (GET /v1/sdr/hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
enabledbodybooleanSwitch this hunter on/off
settingsbodyobjectSubset of the hunter's inputs (see GET /v1/sdr/hunters)

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/hunters/$SOURCE" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "settings": {
    "competitor_pages": [
      "https://www.linkedin.com/company/dentally/",
      "https://www.linkedin.com/company/software-of-excellence/"
    ]
  },
  "enabled": true
}'

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "source": "linkedin_competitor_followers",
    "enabled": true,
    "settings": {
      "competitor_pages": [
        "https://www.linkedin.com/company/dentally/",
        "https://www.linkedin.com/company/software-of-excellence/"
      ],
      "target_regions": [
        "GB"
      ]
    },
    "note": "Settings are search-wide: every hunter that reads the same field sees the new value on its next cycle."
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_configure_hunter

GET/v1/sdr/searches/:id/hunters/:source/dailyanalytics:readnewSignals & contacts per day for one hunter

The hunter page chart (7/14/28 in the UI): signals and contacts per day for one hunter. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcepathstringyesHunter source id (GET /v1/sdr/hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
daysqueryinteger2-60, default 14

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/hunters/$SOURCE/daily" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "source": "linkedin_jobs",
    "days": [
      "2026-10-24",
      "2026-10-25",
      "2026-10-26"
    ],
    "signals": [
      7,
      4,
      6
    ],
    "contacts": [
      3,
      2,
      2
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_hunter_daily

POST/v1/sdr/searches/:id/run-nowaudiences:writeupdatedconfirmRun hunters now

The station "Run" button. With source: that hunter (validated; cross_pool and people_pool are automatic and rejected). Without source: every enabled runnable hunter, as documented (the backend alone defaulted to linkedin_jobs). A running hunter is woken instead of started twice. dry_run lists the hunters with paid-engine / LinkedIn-budget flags.

Credits: No per-run charge; contacts found cost 5 credits each · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcebodystringOne runnable hunter one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
dry_runbodybooleanPreview
confirmbodybooleanExecute

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/run-now" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "ai_web_hunter", "confirm": true}'

Response

json
{
  "data": {
    "ok": true,
    "message": "AI Web Hunter perpetual loop started",
    "source": "ai_web_hunter"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400confirmation_requiredNeither dry_run nor confirm was sent
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_run_now, sdr_run_now

GET/v1/sdr/searches/:id/run-stateanalytics:readPrimary run state for the search

Read-only. Overall run state of a search (running / paused / idle) with the current action.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/run-state" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_run_state

GET/v1/sdr/searches/:id/run-statesanalytics:readPer-source map of hunter states

Per-source map of run state (running / paused / idle) for a Signal Search.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/run-states" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_run_states

POST/v1/sdr/searches/:id/sources/pauseaudiences:writeupdatedPause one hunter

Pauses one hunter; the others keep running (source required and validated; the search is ownership-checked). Response: ok, source, status, "pausing" when the hunter was running and stops at its next checkpoint, "paused" when it was idle (the pause is stored either way), and was (the run status before the call). Fixed (2026-10-26): the raw backend body {accepted, was} used to be passed through.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcebodystringyesHunter source id one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/sources/pause" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "linkedin_activity"}'

Response

json
{
  "data": {
    "ok": true,
    "source": "linkedin_activity",
    "status": "pausing",
    "was": "running"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_pause_hunter

POST/v1/sdr/searches/:id/sources/resumeaudiences:writeupdatedResume one hunter

Resumes one paused hunter (source required and validated). Response: ok, source, status (the hunter's run status after the call, "idle" until its next run) and state (the run-state map of the search, keyed by source). Fixed (2026-10-26): same documented shape as pause.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sourcebodystringyesHunter source id one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/sources/resume" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "linkedin_activity"}'

Response

json
{
  "data": {
    "ok": true,
    "source": "linkedin_activity",
    "status": "idle",
    "state": {
      "linkedin_activity": {
        "search_id": "379007a6-306d-4816-89e5-df48af9958a7",
        "source": "linkedin_activity",
        "status": "idle",
        "phase": null,
        "current_action": null
      }
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_resume_hunter

People (contacts) & ICP QA 14

GET/v1/sdr/peopleaudiences:readupdatedList contacts of a search

Contacts newest first. New filters: group (Fit/Uncertain/Not a fit/unchecked tabs) and source (contacts of one hunter, including pending and phone-only ones, as on the hunter page). Paging up to 200.

Parameters

NameInTypeReq.Description
search_idqueryuuidyesSearch
groupquerystringICP group one of: fits, uncertain, unfit, unchecked
sourcequerystringHunter source one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
companyquerystringCompany name contains
rolequerystringRole category
min_relevancequeryinteger0-100
limitqueryinteger1-200, default 50
offsetqueryintegerPaging offset

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/people?search_id=$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "items": [
      {
        "id": "1f8e2d3c-4b5a-4697-8a1b-2c3d4e5f6a7b",
        "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
        "full_name": "Sarah Collins",
        "title": "Practice Manager",
        "company_name": "Northgate Dental",
        "email": "sarah@northgatedental.co.uk",
        "linkedin_url": "https://www.linkedin.com/in/sarah-collins-dental",
        "icp_group": "fits",
        "enrichment_status": "valid",
        "hot_score": 81,
        "hot_level": 2,
        "detected_at": "2026-10-25T14:20:00Z"
      }
    ],
    "total": 260,
    "limit": 50,
    "offset": 0
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_list_personas

POST/v1/sdr/people/backfill-avatarsaudiences:writeBackfill LinkedIn avatars of a search's contacts

Starts an asynchronous backfill of LinkedIn profile photos onto the contacts of one search (search_id in the body; the search must belong to your workspace).

Parameters

NameInTypeReq.Description
search_idbodystring (uuid)yesSearch of this workspace

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/people/backfill-avatars" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"search_id": "<search_id>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

POST/v1/sdr/people/bulk-deleteaudiences:writenewconfirmRemove several contacts

Multi-select delete of up to 500 contacts of one search. Every id is verified to belong to the search first (404 lists the ones that do not). dry_run counts them by group. No restore.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
search_idbodyuuidyesSearch
idsbodyuuid[]yes1-500 contact ids
dry_runbodybooleanPreview
confirmbodybooleanExecute

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/people/bulk-delete" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
  "ids": [
    "1f8e2d3c-4b5a-4697-8a1b-2c3d4e5f6a7b"
  ],
  "dry_run": true
}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Remove 1 contact(s) from this search (no restore).",
    "estimated": {
      "contacts": 1,
      "by_group": {
        "unfit": 1
      }
    },
    "reversible": false
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400confirmation_requiredNeither dry_run nor confirm was sent
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_bulk_delete_people, sdr_bulk_delete_people

POST/v1/sdr/people/export-to-audienceaudiences:writeupdatedExport contacts to a custom audience

Creates a custom audience from the search's contacts with an email. Fixed: always failed with 400 because the backend needs the user; the user and workspace are now taken from the API key. Adds fit_segment (default fit) and makes name required. No credits.

Parameters

NameInTypeReq.Description
search_idbodyuuidyesSearch
namebodystringyesNew audience name
fit_segmentbodystringDefault fit one of: fit, fit_uncertain, all, uncertain, unfit, unchecked
only_with_emailbodybooleanDefault true
rolesbodystring[]Role categories
idsbodyuuid[]Explicit contacts (overrides roles)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/people/export-to-audience" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
  "name": "Dental owners — Oct",
  "fit_segment": "fit_uncertain"
}'

Response

json
{
  "data": {
    "ok": true,
    "audience_id": "8e9f0a1b-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
    "audience_name": "Dental owners — Oct",
    "contacts_exported": 172,
    "with_email": 172,
    "with_linkedin": 140
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_export_personas_to_audience

GET/v1/sdr/people/export.csvaudiences:readExport contacts of a search as a CSV file (not JSON). Requires search_id; optional company, role, min_relevance. Returns text/csv with a Content-Disposition attachment header. Contacts only, deliberately carries no scores or signal reasoning.

parity with the "Download contacts (CSV)" button. Returns the raw CSV file, NOT the { data } envelope every other route uses. That's deliberate: the whole point is a file you can pipe straight into a spreadsheet or another tool. Declared BEFORE people/:id/detail so Nest doesn't match "export.csv" as an :id. Contacts only, no scores, no signal reasoning. This file leaves our system, so it carries the people data the customer paid for and nothing about how we found them (same rule as the UI export). Export the contacts of an SDR search as CSV text (name, title, email, LinkedIn, location, company). Contacts ONLY, deliberately carries no scores or signal reasoning, because this file leaves the platform. Returns the raw CSV; can be large. Read-only, free.

Parameters

NameInTypeReq.Description
search_idquerystringyes
companyquerystring
rolequerystring
min_relevancequerynumber

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/people/export.csv?search_id=$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above (raw CSV file, no envelope).

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_export_people_csv

GET/v1/sdr/people/group-countsaudiences:readnewFit / Uncertain / Not a fit counts

Counts per ICP group for the tabs and the "Who to contact" choice. only_with_email=true (default) counts email-ready contacts; false counts email-or-LinkedIn reachable ones, as outreach uses. Read-only.

Parameters

NameInTypeReq.Description
search_idqueryuuidyesSearch
only_with_emailquerybooleanDefault true

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/people/group-counts?search_id=$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "fits": 150,
    "uncertain": 42,
    "unfit": 31,
    "unchecked": 37,
    "total": 260
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_people_group_counts

GET/v1/sdr/people/statsaudiences:readupdatedContact stats

Stats strip and chart of the People page for one search. days (7, 14 or 28, default 7) sets the window. Contact counters count email-ready (or phone) contacts that passed quality checks. total = contacts_total (all time); contacts_window_total = new contacts in the window (last_7d_total is the same number under its historical name, it follows days, not always 7 days); daily_series = contacts_daily_series (per day in the window; missing days = 0). signals_* count raw signals; quality_blocked_total, credits_spent, enrichment_breakdown, enriched_total, by_role, top_companies (top 20) with companies_total, and an outreach summary with outreach_daily per channel. Docs fixed (2026-10-26) to the real shape: there is no with_email or daily key.

Parameters

NameInTypeReq.Description
search_idqueryuuidyesSearch
daysqueryinteger7, 14 or 28 (default 7) one of: 7, 14, 28

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/people/stats?search_id=$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (84 lines)
json
{
  "data": {
    "total": 1665,
    "last_7d_total": 862,
    "daily_series": [
      {
        "date": "2026-10-25",
        "count": 131
      },
      {
        "date": "2026-10-26",
        "count": 118
      }
    ],
    "contacts_total": 1665,
    "contacts_window_total": 862,
    "contacts_daily_series": [
      {
        "date": "2026-10-25",
        "count": 131
      },
      {
        "date": "2026-10-26",
        "count": 118
      }
    ],
    "signals_total": 2509,
    "signals_window_total": 1103,
    "signals_daily_series": [
      {
        "date": "2026-10-26",
        "count": 164
      }
    ],
    "quality_blocked_total": 37,
    "credits_spent": 4120,
    "enrichment_breakdown": {
      "valid": 1665
    },
    "enriched_total": 1665,
    "by_role": [
      {
        "role": "owner",
        "count": 412
      },
      {
        "role": "manager",
        "count": 690
      },
      {
        "role": "other",
        "count": 563
      }
    ],
    "companies_total": 1212,
    "top_companies": [
      {
        "company": "Northgate Dental",
        "count": 4
      }
    ],
    "days": 7,
    "outreach": {
      "status": "active",
      "emails_sent": 912,
      "linkedin_sent": 640,
      "replies": 37
    },
    "outreach_daily": {
      "email": [
        {
          "date": "2026-10-26",
          "count": 80
        }
      ],
      "linkedin": [
        {
          "date": "2026-10-26",
          "count": 40
        }
      ]
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_persona_stats

DELETE/v1/sdr/people/:idaudiences:writenewconfirmRemove a contact

Soft-deletes one contact: hidden from every list, count, export and outreach queue. There is no restore route and no refund.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesContact id
search_idbodyuuidyesSearch the contact belongs to (also accepted as query)
dry_runbodybooleanPreview
confirmbodybooleanExecute

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/sdr/people/$PERSON_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10", "confirm": true}'

Response

json
{
  "data": {
    "ok": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400confirmation_requiredNeither dry_run nor confirm was sent
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_delete_person, sdr_delete_person

GET/v1/sdr/people/:id/detailaudiences:readupdatedWhy was this person picked?

Provenance of one contact: hunter, query, triggering signal, scores and the reasoning chain. search_id is now required: membership is proven through the search's contact list because the service has no contact-to-workspace lookup (the previous ownership check always answered 404).

Parameters

NameInTypeReq.Description
idpathuuidyesContact id
search_idqueryuuidyesSearch the contact belongs to

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/people/$PERSON_ID/detail?search_id=$SEARCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (39 lines)
json
{
  "data": {
    "person": {
      "id": "1f8e2d3c-4b5a-4697-8a1b-2c3d4e5f6a7b",
      "full_name": "Sarah Collins",
      "title": "Practice Manager",
      "email": "sarah@northgatedental.co.uk",
      "linkedin_url": "https://www.linkedin.com/in/sarah-collins-dental",
      "location": "Leeds, United Kingdom",
      "enrichment_status": "valid"
    },
    "company": {
      "name": "Northgate Dental",
      "domain": "northgatedental.co.uk",
      "description": null
    },
    "scores": {
      "relevance": 82,
      "icp_fit": 0.86,
      "intent": 0.7,
      "total": 81
    },
    "signal": {
      "type": "hiring_role",
      "topic": "Practice Manager",
      "source": "linkedin_jobs",
      "source_label": "LinkedIn Jobs hunter (hiring signals)",
      "detected_at": "2026-10-24T08:00:00Z"
    },
    "why": [
      {
        "step": "Signal",
        "detail": "Hiring a Practice Manager"
      }
    ],
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "icp_group": "fits"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_person_detail

PATCH/v1/sdr/people/:id/groupaudiences:writenewconfirmMove a contact between ICP groups

Overrides the ICP verdict (fits, uncertain, unfit). Moving to fits a contact the ICP pre-check left free charges 5 credits; that move requires confirm (dry_run shows the cost), and 402 insufficient_credits leaves the group unchanged. Other moves are free and run without confirm. To remove a contact use DELETE.

Credits: 5 credits when a free (pre-check skipped) contact is moved to fits; otherwise 0 · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesContact id
search_idbodyuuidyesSearch the contact belongs to
groupbodystringyesTarget group one of: fits, uncertain, unfit
dry_runbodybooleanPreview
confirmbodybooleanRequired when the move charges credits

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/people/$PERSON_ID/group" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10", "group": "fits", "confirm": true}'

Response

json
{
  "data": {
    "ok": true,
    "group": "fits",
    "credits_charged": 5
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace
400confirmation_requiredThe move charges 5 credits and confirm was not sent
402insufficient_creditsNot enough credits to accept the contact

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_set_person_group, sdr_set_person_group

PATCH/v1/sdr/searches/:id/icp-qa/autocorrectaudiences:writenewKeyword auto-correction on/off

Allows the ICP check to rewrite the search's industry keywords automatically (default off). Roll back any change with icp-qa/rollback.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
enabledbodybooleanyesOn/off

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-qa/autocorrect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

Response

json
{
  "data": {
    "ok": true,
    "enabled": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_qa_autocorrect

GET/v1/sdr/searches/:id/icp-qa/historyaudiences:readnewICP check history

Past QA runs for the quality chart and Search-history tab, newest first, plus whether keyword auto-correction is on. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
limitqueryinteger1-100, default 30

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-qa/history" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "runs": [
      {
        "id": "7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
        "checked_count": 40,
        "fits_count": 25,
        "uncertain_count": 9,
        "unfit_count": 6,
        "quality_pct": 63,
        "created_at": "2026-10-26T11:10:00Z"
      }
    ],
    "autocorrect_enabled": false
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_qa_history

POST/v1/sdr/searches/:id/icp-qa/rollbackaudiences:writenewRoll keywords back to a QA run

Restores the search's industry_keywords to the snapshot taken before the given QA run (undo of an auto-correction). The run must belong to this search.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
run_idbodyuuidyesQA run id from history

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-qa/rollback" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"run_id": "7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"}'

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "run_id": "7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    "industry_keywords": [
      "dental clinic"
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_qa_rollback

POST/v1/sdr/searches/:id/icp-qa/runaudiences:writenewICP check now

People page "Check now": classifies the newest unchecked contacts as Fit / Uncertain / Not a fit with AI (not billed) and records a QA run (quality %, keyword suggestions). run is null when nothing is unchecked or the search has no ICP summary. Up to ~2 minutes.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-qa/run" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "run": {
      "id": "7c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
      "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
      "checked_count": 40,
      "fits_count": 25,
      "uncertain_count": 9,
      "unfit_count": 6,
      "quality_pct": 63,
      "keywords_before": [
        "dental clinic"
      ],
      "keywords_after": [
        "dental clinic",
        "dental practice"
      ],
      "created_at": "2026-10-26T11:10:00Z"
    }
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_qa_run

Outreach 11

GET/v1/sdr/outreach/sales-processcampaigns:readCompleted SDR outreach campaigns for Sales Process

Read-only. SDR outreach processes of the workspace as listed on the Sales Process page (status, channels, counters).

Parameters

NameInTypeReq.Description
agent_idquerystring

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/outreach/sales-process" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_list_outreach_processes

GET/v1/sdr/searches/:id/outreachcampaigns:readGet outreach status for one Signal Search

Read the SDR outreach campaign, channels enabled, ready/queued/sent counts, current content config.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_outreach

DELETE/v1/sdr/searches/:id/outreachcampaigns:writeconfirmDelete outreach campaign (paired dry_run/confirm)

Delete the SDR outreach campaign. The Signal Search and its hunters CONTINUE running. Only call after sdr_preview_delete_outreach.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_delete_outreach, sdr_delete_outreach

GET/v1/sdr/searches/:id/outreach/contentcampaigns:readRead outreach templates

Read the configured email subject/body templates and LinkedIn invite template for SDR outreach.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/content" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_get_outreach_content

PATCH/v1/sdr/searches/:id/outreach/contentcampaigns:writeupdatedEdit outreach messages

Outreach windows 2-3, now with an explicit allow-list: email {mode ai|fixed|html, prompt, tone, variants ≤5 {subject, body}, followups ≤7 {delay_days, prompt|subject+body}, html_steps, signature, auto-reply…} and linkedin {invite on/off, invite_mode/followup_mode ai|exact with texts, followup_delay_minutes, conversation_rules ≤3000, auto-reply delays, signature_mode, calendly_url…}. Placeholders: {{first_name}} {{last_name}} {{full_name}} {{company_name}} {{position}} {{city}} {{country}}; a LinkedIn message with an unresolved {{…}} is not sent. No model call on save.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
emailbodyobjectEmail settings (partial)
linkedinbodyobjectLinkedIn settings (partial)

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/content" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": {
    "mode": "fixed",
    "variants": [
      {
        "subject": "{{company_name}} recalls",
        "body": "Hi {{first_name}}, …"
      }
    ],
    "followups": [
      {
        "delay_days": 3,
        "subject": "Re: recalls",
        "body": "Quick follow-up, {{first_name}}"
      }
    ]
  },
  "linkedin": {
    "invite_enabled": true,
    "invite_mode": "ai",
    "conversation_rules": "Goal: a 15-minute demo. Offer https://cal.com/brightsmile/demo."
  }
}'

Response

Show the full response (35 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "campaign_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "channels": [
      "email",
      "linkedin"
    ],
    "config": {
      "email": {
        "mode": "fixed",
        "variants": [
          {
            "subject": "{{company_name}} recalls",
            "body": "Hi {{first_name}}, …"
          }
        ],
        "max_followups": 1,
        "followup_interval_days": 3
      },
      "linkedin": {
        "invite_enabled": true,
        "invite_mode": "ai",
        "followup_delay_minutes": 0,
        "reply_delay_min_minutes": 2,
        "reply_delay_max_minutes": 7
      }
    },
    "default_email_signature": "Anna Smith\nBrightsmile Software",
    "preview": {
      "samples": [],
      "cached": false
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_update_outreach_content

POST/v1/sdr/searches/:id/outreach/content/previewcampaigns:readconfirmRender a personalised message preview

Render an example personalized email/LinkedIn message for a specific contact. Costs ~1 Claude call.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/content/preview" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_outreach_content

POST/v1/sdr/searches/:id/outreach/launchcampaigns:controlupdatedconfirmLaunch SDR outreach

"Save and Launch" with the full contract: every window-1 field (incl. icp_segment, LinkedIn accounts, hours, time zone, CRM branch) is validated; omitted fields come from the saved settings, then the defaults, like the UI. sender_mode is rejected (use email_sender_mode). dry_run now reports the effective settings, contacts in the segment, ready email/LinkedIn contacts, predicted blockers, the paid-plan rule and the delivery window. Real messages start after confirm.

Credits: 5 credits per newly queued contact not charged before · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
channelsbodystring[]Subset of email, linkedin. Default: saved value, else both one of: email, linkedin
email_sender_modebodystringmanaged = Vendo sender pool (paid plans only, else 403); client_owned = the workspace's own mailboxes (connect them via /v1/email/mailboxes). Default: saved value, else client_owned when a mailbox exists, else managed one of: managed, client_owned
client_email_providerbodystring|nullgoogle or microsoft (client_owned only) one of: google, microsoft
selected_linkedin_account_idsbodyuuid[]LinkedIn accounts to send from (ids from GET …/outreach linkedin_accounts). Empty = every connected account in status OK
timezonebodystringIANA time zone of the working window, default UTC
work_start_hourbodyinteger0-23, default 9
work_end_hourbodyinteger0-23, default 18
exclude_weekendsbodybooleanDefault true
daily_email_limitbodyinteger1-500, default 50
daily_linkedin_limitbodyinteger1-200, default 40
branch_idbodyuuid|null|'general'CRM branch that receives replies; null or 'general' = General. Must be a branch you can see (GET /v1/crm/branches; create one with POST /v1/crm/branches)
icp_segmentbodystringWho to contact: fit (Fit + unchecked, default), fit_uncertain, all; legacy uncertain, unfit, unchecked one of: fit, fit_uncertain, all, uncertain, unfit, unchecked
dry_runbodybooleanPreview
confirmbodybooleanExecute

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/launch" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "channels": [
    "email",
    "linkedin"
  ],
  "email_sender_mode": "client_owned",
  "client_email_provider": "google",
  "selected_linkedin_account_ids": [
    "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"
  ],
  "timezone": "Europe/London",
  "work_start_hour": 9,
  "work_end_hour": 17,
  "exclude_weekends": true,
  "daily_email_limit": 50,
  "daily_linkedin_limit": 30,
  "branch_id": null,
  "icp_segment": "fit",
  "confirm": true
}'

Response

Show the full response (49 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "delivery_window": {
      "sending_now": true,
      "next_send_at": null,
      "timezone": "Europe/London",
      "work_start_hour": 9,
      "work_end_hour": 17,
      "exclude_weekends": true,
      "queued": 48
    },
    "min_contacts_required": 1,
    "ready_contacts": 190,
    "linkedin_ready_contacts": 140,
    "can_launch": true,
    "campaign": {
      "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "status": "active",
      "channels": [
        "email",
        "linkedin"
      ],
      "email_sender_mode": "client_owned",
      "timezone": "Europe/London",
      "work_start_hour": 9,
      "work_end_hour": 17,
      "exclude_weekends": true,
      "branch_id": null,
      "settings": {
        "icp_segment": "fit",
        "blocked_reasons": []
      }
    },
    "linkedin_accounts": [
      {
        "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
        "name": "Anna Smith",
        "status": "OK",
        "can_send": true
      }
    ],
    "client_mailboxes": {
      "ready": 2,
      "warming": 1,
      "error": 0
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400confirmation_requiredNeither dry_run nor confirm was sent
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace
400invalid_requestFewer ready contacts than the launch minimum, or no channel
403forbiddenManaged senders on a workspace without a paid plan

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_preview_launch_outreach, sdr_launch_outreach

POST/v1/sdr/searches/:id/outreach/linkedin/connectcampaigns:writeLink LinkedIn accounts to this outreach

Returns a hosted link where the user connects a LinkedIn account for this SDR's outreach. A human must open it; then pick the account in selected_linkedin_account_ids.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/linkedin/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_linkedin_connect_link

POST/v1/sdr/searches/:id/outreach/pausecampaigns:controlPause outreach delivery

Stop sending new messages on this SDR campaign. Reversible.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/pause" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_pause_outreach

POST/v1/sdr/searches/:id/outreach/resumecampaigns:controlResume outreach delivery

Resume a paused SDR outreach campaign.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/resume" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_resume_outreach

PATCH/v1/sdr/searches/:id/outreach/settingscampaigns:writenewSave outreach settings without launching

Outreach window 1 "Save": Who to contact, channels, sender mode and provider, LinkedIn accounts, time zone, hours, weekends, daily limits and CRM branch. Partial: omitted fields keep their saved values (else defaults). Never launches: a new process stays draft; a running one keeps its status and applies the change at once, widening icp_segment on a running process queues more contacts (5 credits each). Own mailboxes are managed via /v1/email/mailboxes.

Credits: 0 for a draft; on a running process newly queued contacts cost 5 credits each

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
channelsbodystring[]Subset of email, linkedin. Default: saved value, else both one of: email, linkedin
email_sender_modebodystringmanaged = Vendo sender pool (paid plans only, else 403); client_owned = the workspace's own mailboxes (connect them via /v1/email/mailboxes). Default: saved value, else client_owned when a mailbox exists, else managed one of: managed, client_owned
client_email_providerbodystring|nullgoogle or microsoft (client_owned only) one of: google, microsoft
selected_linkedin_account_idsbodyuuid[]LinkedIn accounts to send from (ids from GET …/outreach linkedin_accounts). Empty = every connected account in status OK
timezonebodystringIANA time zone of the working window, default UTC
work_start_hourbodyinteger0-23, default 9
work_end_hourbodyinteger0-23, default 18
exclude_weekendsbodybooleanDefault true
daily_email_limitbodyinteger1-500, default 50
daily_linkedin_limitbodyinteger1-200, default 40
branch_idbodyuuid|null|'general'CRM branch that receives replies; null or 'general' = General. Must be a branch you can see (GET /v1/crm/branches; create one with POST /v1/crm/branches)
icp_segmentbodystringWho to contact: fit (Fit + unchecked, default), fit_uncertain, all; legacy uncertain, unfit, unchecked one of: fit, fit_uncertain, all, uncertain, unfit, unchecked
dry_runbodybooleanPreview the effective settings and readiness

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "icp_segment": "fit_uncertain",
  "timezone": "Europe/London",
  "work_start_hour": 8,
  "work_end_hour": 17
}'

Response

Show the full response (41 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "delivery_window": null,
    "min_contacts_required": 1,
    "ready_contacts": 190,
    "linkedin_ready_contacts": 140,
    "can_launch": true,
    "campaign": {
      "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "status": "draft",
      "channels": [
        "email",
        "linkedin"
      ],
      "email_sender_mode": "client_owned",
      "timezone": "Europe/London",
      "work_start_hour": 9,
      "work_end_hour": 17,
      "exclude_weekends": true,
      "branch_id": null,
      "settings": {
        "icp_segment": "fit",
        "blocked_reasons": []
      }
    },
    "linkedin_accounts": [
      {
        "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
        "name": "Anna Smith",
        "status": "OK",
        "can_send": true
      }
    ],
    "client_mailboxes": {
      "ready": 2,
      "warming": 1,
      "error": 0
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
400forbidden_fieldThe body carries a field that cannot be set here
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_save_outreach_settings

Search analytics 10

GET/v1/sdr/searches/:id/companies/export.csvaudiences:readnewExport companies (CSV)

Raw CSV (not the {data} envelope) of every company the search found: Company, Domain, Heat, ICP fit, Signal count, Signal types, Contact status, Contacts found. UTF-8 with BOM for Excel.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/companies/export.csv" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "csv": "Company,Domain,Heat,ICP fit,Signal count,Signal types,Contact status,Contacts found\r\nNorthgate Dental,northgatedental.co.uk,82,fit,3,hiring_role | news_mention,Contact found,2"
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_export_companies_csv

GET/v1/sdr/searches/:id/companies/:companyIdanalytics:readnewHot company popup

One company of a search: card from the companies database (website, logo, size, location, links), decision-makers found with fit, other known people at the company and its live signals. part=card returns only the card and people pool and may wait up to 20 s; the default full view answers in ~2.5-6.5 s with the card when available.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
companyIdpathuuidyescompany_id from company-heat
partquerystringfull (default) or card one of: full, card
namequerystringCompany name hint for domain resolution
domainquerystringDomain hint

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/companies/$COMPANY_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (44 lines)
json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "company_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    "part": "full",
    "card_ready": true,
    "resolved_domain": "northgatedental.co.uk",
    "heat": 82,
    "company": {
      "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
      "name": "Northgate Dental",
      "website": "https://northgatedental.co.uk",
      "logo_url": null,
      "description": "Family dental practice in Leeds.",
      "industry": "Dental",
      "country": "GB",
      "city": "Leeds",
      "employee_count": 18,
      "linkedin_url": null,
      "tags": []
    },
    "people": [
      {
        "full_name": "Sarah Collins",
        "title": "Practice Manager",
        "icp_group": "fits"
      }
    ],
    "pool_people": [
      {
        "full_name": "Tom Reed",
        "title": "Dentist",
        "linkedin_url": "https://www.linkedin.com/in/tomreed"
      }
    ],
    "signals": [
      {
        "signal_type": "hiring_role",
        "topic": "Practice Manager",
        "detected_at": "2026-10-24T08:00:00Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_company_detail

GET/v1/sdr/searches/:id/eventsanalytics:readnewLive hunter events (polling)

Polling replacement for the search page's live stream: connector start/finish, queries, companies resolved, signals saved, contacts created/enriched. Pass since = next_since from the previous call to get only newer events. Best effort: the last 150 events per search are kept in memory and lost on a service restart.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
sincequerydatetimeISO timestamp; only newer events
limitqueryinteger1-200, default 100

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/events" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "events": [
      {
        "id": "ev_9f2c",
        "ts": "2026-10-26T11:02:03.120Z",
        "kind": "signal_saved",
        "severity": "success",
        "source": "linkedin_jobs",
        "message": "Northgate Dental is hiring a Practice Manager"
      }
    ],
    "next_since": "2026-10-26T11:02:03.120Z",
    "buffer_note": "Best-effort live feed: the last 150 events per search are kept in memory and are lost on a service restart."
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_events

GET/v1/sdr/searches/:id/icp-reviewanalytics:readnewICP Review panel

Uncertain contacts waiting for re-check, results of the last pass, totals and the next scheduled run (weekly). Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-review" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "pending": 34,
    "lastRunAt": "2026-10-25T06:00:00Z",
    "lastRunFits": 11,
    "lastRunUnfit": 7,
    "lastRunForced": false,
    "totalReviewed": 120,
    "totalFits": 46,
    "nextRunAt": "2026-11-01T06:00:00Z"
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_review

POST/v1/sdr/searches/:id/icp-review/runaudiences:writenewRe-check Uncertain contacts now

"Re-check now": re-judges this search's Uncertain contacts with the same bounded pass as the weekly job; a second run while one is in flight is refused by the service. Contacts moved to Fit become billable (5 credits) when used. May take up to ~4 minutes.

Credits: No charge for the check; contacts moved to Fit cost 5 credits when used

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/icp-review/run" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "ok": true,
    "checked": 34,
    "fits": 11,
    "unfit": 7
  }
}

Errors

HTTPCodeWhen
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_icp_review_run

POST/v1/sdr/searches/:id/learning-recomputeaudiences:writeManually trigger the learning loop

Recalculate per-signal-type weights from outreach outcomes (replies/meetings vs sends) for one search. Safe, no cost; use when the user asks to refresh/recalculate Signal Learning.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/learning-recompute" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_learning_recompute

GET/v1/sdr/searches/:id/outreach/activity-feedanalytics:readRecent discovery + outreach events for a search, newest first. Optional limit (default 40).

Live activity ticker for one search, the merged discovery + outreach feed the dashboard polls (Phase 7). Polling, not SSE: streaming over an API key needs its own auth/backpressure design, and this answers the same question for integrators. Recent discovery + outreach events for one SDR search, newest first, the same ticker the dashboard shows. Read-only, free.

Parameters

NameInTypeReq.Description
idpathstringyes
limitquerynumberDefault 40

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/activity-feed" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_activity_feed

GET/v1/sdr/searches/:id/outreach/summaryanalytics:readSent / queued counters per channel for a search.

Sent / queued counters per channel for one search. Per-channel sent and queued counts for one SDR search. Read-only, free.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/outreach/summary" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_outreach_summary

GET/v1/sdr/searches/:id/statsanalytics:readupdatedSignal stats by hunter

KPI cards and raw sources of a search. The window parameter is now days (1-90, default 30) and is actually applied (days_back was ignored by the backend; it is still accepted as an alias).

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
daysqueryinteger1-90, default 30

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "days_back": 30,
    "fresh_signals_total": 702,
    "signal_sources": [],
    "pipeline_sources": [
      {
        "source": "connected_people",
        "count": 260
      },
      {
        "source": "email_enricher",
        "count": 190
      }
    ],
    "by_source": [
      {
        "source": "linkedin_jobs",
        "count": 182
      }
    ],
    "generated_at": "2026-10-26T11:00:00Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_signal_stats

GET/v1/sdr/searches/:id/stats/dailyanalytics:readnewSignals per day per hunter

Station sparklines: signals per day for every hunter of a search. Read-only.

Parameters

NameInTypeReq.Description
idpathuuidyesSDR search (process) id
daysqueryinteger2-60, default 14

Request

curl
curl -s "https://api.vendo-ai.com/v1/sdr/searches/$SEARCH_ID/stats/daily" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "search_id": "9b1f0c3e-6a52-4d7e-9a43-2f6c1d8e7a10",
    "days": [
      "2026-10-25",
      "2026-10-26"
    ],
    "series": {
      "linkedin_jobs": [
        7,
        6
      ],
      "crunchbase_rss": [
        2,
        0
      ]
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter is missing or invalid (message names it)
404not_foundThe search/contact/job does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sdr_daily_by_hunter

Campaigns API 66

Classic multi-channel campaigns: lifecycle, wizard previews, emails and replies, AI conversation threads, LinkedIn, mailboxes and senders, phone campaigns, monitoring and content accounts.

Lifecycle 12

GET/v1/campaignscampaigns:readupdatedList campaigns

Lists classic Sales Process campaigns of the workspace with status and live stats. Archived campaigns are hidden unless include_archived=true, like the Archived checkbox in the app.

Parameters

NameInTypeReq.Description
include_archivedquerybooleanAlso return archived campaigns.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (70 lines)
json
{
  "data": [
    {
      "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
      "name": "UK SaaS founders — Q4",
      "status": "active",
      "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
      "branch_id": null,
      "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "settings": {
        "channels": [
          "email",
          "linkedin"
        ],
        "dailyLimit": 80,
        "dailyEmailLimit": 80,
        "dailyLinkedinLimit": 40,
        "timezone": "Europe/London",
        "workingHours": [
          9,
          18
        ],
        "excludeWeekends": true,
        "emailSenderMode": "client_owned",
        "trackOpens": true,
        "emailContent": {
          "mode": "ai",
          "prompt": "Short, specific, one question.",
          "tone": "friendly",
          "followUps": [
            {
              "delayDays": 3,
              "prompt": "New angle: case study"
            }
          ]
        },
        "linkedinContent": {
          "mode": "ai",
          "invite_tone": "friendly",
          "followup_delay_minutes": 60
        },
        "selectedLinkedinAccountIds": [
          "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
        ],
        "customInstructions": "Offer a 15-minute call. Use the formal form of address."
      },
      "sequence": [
        {
          "step": 1,
          "delayDays": 0
        },
        {
          "step": 2,
          "delayDays": 3
        }
      ],
      "stats": {
        "totalContacts": 412,
        "emailsSent": 0,
        "emailsOpened": 0,
        "linksClicked": 0,
        "repliesReceived": 0,
        "meetingsBooked": 0
      },
      "created_at": "2026-10-26T09:12:44.000Z",
      "updated_at": "2026-10-26T09:14:02.000Z"
    }
  ],
  "has_more": false
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaigns

POST/v1/campaignscampaigns:writeupdatedCreate a campaign (draft)

Creates a campaign in DRAFT status; nobody is contacted until launch. Accepts every wizard setting; settings the backend create step does not store (trackOpens, trackLinks, stopOnNegative, speedTier) are applied right after create, as the app autosave does. agentId, branchId, selectedLinkedinAccountIds and senderEmailIds are checked against the workspace. customAudienceId builds the campaign audience from a READY uploaded/CRM list.

Parameters

NameInTypeReq.Description
namebodystringyesCampaign name (≤200).
audienceIdbodyuuidCampaign audience id (POST /v1/audiences). Required unless customAudienceId is given.
customAudienceIdbodyuuidId of a READY custom audience; a campaign audience is created from it.
agentIdbodyuuidSales agent. Required before launch (AGENT_REQUIRED).
branchIdbodyuuid|nullCRM branch for replies; null = General.
descriptionbodystringFree text; generated from the audience when omitted.
settingsbodyobjectWizard settings (all optional, merged on update). channels [email|linkedin|phone_call]; dailyLimit (new contacts/day, ≤ sender capacity); dailyEmailLimit; dailyLinkedinLimit (campaign-wide invites/day across all accounts); timezone (IANA); workingHours [start 0–23, end 1–24]; excludeWeekends; campaignDurationDays; customInstructions (Communication Rules ≤6000: CTA, links, promises; also sets the ти/Ви, du/Sie form of address); emailSignature (HTML); notifyEmails[]; ccEmail; autoResponseEnabled; autoResponseDelayMinutes; followUpInterval & maxFollowUps (legacy, used when emailContent.followUps is empty); trackOpens; trackLinks; stopOnNegative; speedTier [conservative|balanced|aggressive]; emailSenderMode [managed|client_owned] (managed needs a paid plan to launch); clientEmailProvider [google|microsoft|null]; senderEmailIds[] (ids from GET /v1/email/senders; empty = auto-assign); emailContent {mode ai|fixed|html, prompt, tone formal|casual|friendly, variants ≤5 {subject, body}, htmlSteps ≤20 {subject, html, delayDays}, followUps ≤7 {delayDays 1–90, prompt | subject+body}}; linkedinContent {mode ai|fixed, invite_prompt, invite_tone, invite_exact_text ≤300, followup_delay_minutes 0–43200, followup_exact_text ≤1500}; selectedLinkedinAccountIds[] (ids from GET /v1/linkedin/accounts; empty = all connected at launch); linkedinAutoReplyDisabled. Other keys are ignored and listed in ignored_settings.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "UK SaaS founders — Q4",
  "audienceId": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
  "agentId": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
  "settings": {
    "channels": [
      "email",
      "linkedin"
    ],
    "dailyLimit": 80,
    "dailyEmailLimit": 80,
    "dailyLinkedinLimit": 40,
    "timezone": "Europe/London",
    "workingHours": [
      9,
      18
    ],
    "excludeWeekends": true,
    "emailSenderMode": "client_owned",
    "trackOpens": true,
    "emailContent": {
      "mode": "ai",
      "prompt": "Short, specific, one question.",
      "tone": "friendly",
      "followUps": [
        {
          "delayDays": 3,
          "prompt": "New angle: case study"
        }
      ]
    },
    "linkedinContent": {
      "mode": "ai",
      "invite_tone": "friendly",
      "followup_delay_minutes": 60
    },
    "selectedLinkedinAccountIds": [
      "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
    ],
    "customInstructions": "Offer a 15-minute call. Use the formal form of address."
  }
}'

Response

Show the full response (67 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "draft",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundAgent, branch, LinkedIn account, sender or custom audience is not in the workspace.
409audience_not_readyThe custom audience is still processing.
503provider_unavailablesenderEmailIds cannot be validated because the sender list is temporarily unavailable.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_campaign

GET/v1/campaigns/:idcampaigns:readGet campaign details + stats

(include_archived), POST, PATCH :id, launch, pause, pause-channel, resume-channel and archive moved to v2/campaigns.controller.ts (Round-6, 2026-10-26: allow-listed settings, ownership checks, clear 4xx for business failures, UI-identical launch). Full campaign: status, settings (all wizard fields), sequence, channel paused flags, channelStatuses, today's sends, stats.totalContacts. Read-only.

Parameters

NameInTypeReq.Description
idpathstringyesCampaign id (UUID) from list_campaigns

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign

PATCH/v1/campaigns/:idcampaigns:writeupdatedUpdate campaign settings

Partial update of name, description and wizard settings (settings are merged). agentId and branchId can change only while the campaign is a draft. On a running campaign a change to email steps or delays is not applied once anything was sent; the pending change is visible as settings.smartleadSequencePending. Internal settings keys can no longer be written.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
namebodystringNew name.
descriptionbodystring
agentIdbodyuuidDraft only.
branchIdbodyuuid|nullDraft only; null = General.
settingsbodyobjectWizard settings (all optional, merged on update). channels [email|linkedin|phone_call]; dailyLimit (new contacts/day, ≤ sender capacity); dailyEmailLimit; dailyLinkedinLimit (campaign-wide invites/day across all accounts); timezone (IANA); workingHours [start 0–23, end 1–24]; excludeWeekends; campaignDurationDays; customInstructions (Communication Rules ≤6000: CTA, links, promises; also sets the ти/Ви, du/Sie form of address); emailSignature (HTML); notifyEmails[]; ccEmail; autoResponseEnabled; autoResponseDelayMinutes; followUpInterval & maxFollowUps (legacy, used when emailContent.followUps is empty); trackOpens; trackLinks; stopOnNegative; speedTier [conservative|balanced|aggressive]; emailSenderMode [managed|client_owned] (managed needs a paid plan to launch); clientEmailProvider [google|microsoft|null]; senderEmailIds[] (ids from GET /v1/email/senders; empty = auto-assign); emailContent {mode ai|fixed|html, prompt, tone formal|casual|friendly, variants ≤5 {subject, body}, htmlSteps ≤20 {subject, html, delayDays}, followUps ≤7 {delayDays 1–90, prompt | subject+body}}; linkedinContent {mode ai|fixed, invite_prompt, invite_tone, invite_exact_text ≤300, followup_delay_minutes 0–43200, followup_exact_text ≤1500}; selectedLinkedinAccountIds[] (ids from GET /v1/linkedin/accounts; empty = all connected at launch); linkedinAutoReplyDisabled. Other keys are ignored and listed in ignored_settings.

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings": {"dailyLinkedinLimit": 30, "trackOpens": false}}'

Response

Show the full response (67 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "draft",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 30,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": false,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.
409not_draftagentId/branchId sent for a campaign that is no longer a draft.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_campaign

DELETE/v1/campaigns/:idcampaigns:writeconfirmDelete campaign (requires confirm)

destructive. dry_run / confirm required. Permanently delete a campaign. NOT reversible. Only call after the user has reviewed preview_delete_campaign output and explicitly approved.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_campaign, delete_campaign

POST/v1/campaigns/:id/archivecampaigns:controlupdatedconfirmArchive a campaign

Archives the campaign: status becomes completed, all outreach stops (an active campaign releases its mailboxes and LinkedIn accounts) and it is hidden from the default list (GET /v1/campaigns?include_archived=true shows it). Not reversible, duplicate to run it again. Fixed (2026-10-26): {"dry_run": true} was ignored and archived for real; it now returns a preview (status, channels, what stops, what is kept) and changes nothing. {"confirm": true} archives.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/archive" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Show the full response (68 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "completed",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z",
    "is_archived": true
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.
409already_archivedAlready archived.
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
400invalid_requestBody has fields other than dry_run / confirm

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_archive_campaign, archive_campaign

POST/v1/campaigns/:id/duplicatecampaigns:writenewDuplicate a campaign

Copies a campaign (settings + a new audience) into a new DRAFT named "<name> (Copy)". Nothing is sent, no credits. The backend copies company-based audiences only and no CRM branch; check the copy before launching.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/duplicate" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (67 lines)
json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "name": "UK SaaS founders — Q4 (Copy)",
    "status": "draft",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: duplicate_campaign

POST/v1/campaigns/:id/launchcampaigns:controlupdatedconfirmLaunch or resume a campaign

dry_run returns a readiness report computed without side effects: ready flag, blockers (AGENT_REQUIRED, AUDIENCE_REQUIRED, CLIENT_MAILBOX_NOT_CONNECTED, PAID_PLAN_REQUIRED for Vendo managed senders, INVALID_STATUS), warnings, the real recipient count, limits, sender mode, schedule and the LinkedIn accounts to activate. confirm does what the app does: starts the campaign, then queues LinkedIn on the selected (or all connected) accounts when LinkedIn is a channel and nothing is queued yet. A LinkedIn failure does not fail the launch. Real outreach starts in the working hours; credits are spent as messages go out. New blocker AUDIENCE_EMPTY (2026-10-26): the audience has 0 contacts, the dry run used to say ready: true although the launch then failed with 422.

Credits: Per message sent (email/LinkedIn per pricing) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.
linkedin_account_idsbodyuuid[]Override the LinkedIn accounts to activate (default: settings.selectedLinkedinAccountIds, else all connected).

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/launch" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Show the full response (57 lines)
json
{
  "data": {
    "dry_run": true,
    "would_do": "Launch campaign \"UK SaaS founders — Q4\"",
    "ready": true,
    "blockers": [],
    "warnings": [
      {
        "code": "MAILBOXES_WARMING",
        "message": "Your mailboxes are still warming up; sending starts as they become ready."
      }
    ],
    "campaign": {
      "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
      "name": "UK SaaS founders — Q4",
      "status": "draft"
    },
    "recipient_count": 412,
    "channels": [
      "email",
      "linkedin"
    ],
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "daily_limit": 80,
    "daily_email_limit": 80,
    "daily_linkedin_limit": 40,
    "email_sender_mode": "client_owned",
    "client_mailboxes": {
      "usable": 2,
      "ready": 0
    },
    "email_steps": 2,
    "email_content_mode": "ai",
    "track_opens": true,
    "schedule": {
      "timezone": "Europe/London",
      "working_hours": [
        9,
        18
      ],
      "exclude_weekends": true,
      "duration_days": 30
    },
    "linkedin": {
      "accounts": [
        {
          "id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
          "name": "Anna Clarke"
        }
      ],
      "source": "settings.selectedLinkedinAccountIds",
      "already_queued": 0,
      "daily_linkedin_limit": 40,
      "auto_replies_disabled": false
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
404not_foundCampaign does not exist or belongs to another workspace.
409agent_requiredconfirm on a campaign without agent.
409client_mailbox_not_connectedclient_owned mode without a warming/ready own mailbox.
403paid_plan_requiredManaged senders on a free plan.
409invalid_statusStatus is not draft/paused/active.
422audience_requiredNo audience.
422launch_failedScheduling failed (e.g. no contacts with email and no LinkedIn queue); the campaign stays draft.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_launch_campaign, launch_campaign

POST/v1/campaigns/:id/pausecampaigns:controlupdatedPause a campaign

Pauses every channel of an active campaign. Resume with launch. Business failures now answer 409 instead of 500.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/pause" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (67 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "paused",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.
409not_activeCampaign is not active.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: pause_campaign

POST/v1/campaigns/:id/pause-channelcampaigns:controlupdatedPause one channel

Pauses one channel of an active campaign; the others keep going. A missing or unknown channel is now a 400 (was HTTP 200 with an error body).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
channelbodystringyesChannel to pause. one of: email, linkedin, phone_call, voice_call

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/pause-channel" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel": "linkedin"}'

Response

Show the full response (68 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "active",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z",
    "linkedin_channel_paused": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.
409not_activeCampaign is not active.
409already_pausedChannel already paused.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: pause_channel

POST/v1/campaigns/:id/resume-channelcampaigns:controlupdatedResume one channel

Resumes a paused channel. Campaigns auto-paused for insufficient credits stay paused until the balance is positive (400 from the backend).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
channelbodystringyesChannel to resume. one of: email, linkedin, phone_call, voice_call

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/resume-channel" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel": "linkedin"}'

Response

Show the full response (68 lines)
json
{
  "data": {
    "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "name": "UK SaaS founders — Q4",
    "status": "active",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "branch_id": null,
    "audience_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "settings": {
      "channels": [
        "email",
        "linkedin"
      ],
      "dailyLimit": 80,
      "dailyEmailLimit": 80,
      "dailyLinkedinLimit": 40,
      "timezone": "Europe/London",
      "workingHours": [
        9,
        18
      ],
      "excludeWeekends": true,
      "emailSenderMode": "client_owned",
      "trackOpens": true,
      "emailContent": {
        "mode": "ai",
        "prompt": "Short, specific, one question.",
        "tone": "friendly",
        "followUps": [
          {
            "delayDays": 3,
            "prompt": "New angle: case study"
          }
        ]
      },
      "linkedinContent": {
        "mode": "ai",
        "invite_tone": "friendly",
        "followup_delay_minutes": 60
      },
      "selectedLinkedinAccountIds": [
        "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"
      ],
      "customInstructions": "Offer a 15-minute call. Use the formal form of address."
    },
    "sequence": [
      {
        "step": 1,
        "delayDays": 0
      },
      {
        "step": 2,
        "delayDays": 3
      }
    ],
    "stats": {
      "totalContacts": 412,
      "emailsSent": 0,
      "emailsOpened": 0,
      "linksClicked": 0,
      "repliesReceived": 0,
      "meetingsBooked": 0
    },
    "created_at": "2026-10-26T09:12:44.000Z",
    "updated_at": "2026-10-26T09:14:02.000Z",
    "linkedin_channel_paused": false
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.
409not_pausedChannel is not paused.
409invalid_statusCampaign is neither active nor paused.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: resume_channel

POST/v1/campaigns/:id/sync-statscampaigns:writenewSync email counters

Pulls sent/opened/clicked/replied/bounced counters from the sending provider and stores them. Live provider call: limited to once per minute per campaign.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/sync-stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "synced": true,
    "stats": {
      "sent": 1260,
      "opened": 512,
      "clicked": 41,
      "replied": 37,
      "bounced": 9
    },
    "diff": {
      "sent": 12,
      "opened": 5,
      "clicked": 0,
      "replied": 1,
      "bounced": 0
    }
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.
409not_sent_yetCampaign never sent email.
429rate_limitedCalled again within 60 s.
502provider_unavailableProvider did not answer.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: sync_campaign_stats

Wizard previews 3

POST/v1/campaigns/preview/emailcampaigns:readnewPreview a campaign email

Renders the exact first email or follow-up #k a campaign would send, with its writing rules, tone, Communication Rules or fixed text. With campaign_id the saved settings are used and any field you pass overrides them. One AI call on our side; no sending, no credits. Test keys allowed.

Parameters

NameInTypeReq.Description
campaign_idbodyuuidUse this campaign's saved settings and agent.
agent_idbodyuuidAgent for AI mode (required without campaign_id).
modebodystringai or fixed. one of: ai, fixed
promptbodystringAI writing rules (≤4000).
tonebodystringone of: formal, casual, friendly
custom_instructionsbodystringCommunication Rules (≤6000).
variantbodyobjectFixed mode {subject, body}; placeholders like {{first_name}} are filled.
follow_upbodyobject{index 1–7, delay_days 1–90, previous {subject, body}}, preview follow-up #index.
samplebodyobject{contact_name, company_name, position, city, country, website} sample recipient.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/preview/email" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15", "follow_up": {"index": 1}}'

Response

json
{
  "data": {
    "mode": "ai",
    "subject": "Re: quicker onboarding at Acme Corp",
    "body": "Hi Alex,\n\nFollowing up on my note from Monday — one thing teams like yours found useful...",
    "follow_up_index": 1
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or agent not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_campaign_email

POST/v1/campaigns/preview/linkedin-followupcampaigns:readnewPreview the LinkedIn message after connect

Renders the first LinkedIn message sent after an invite is accepted: AI mode uses the agent's follow-up generator, fixed mode fills the placeholders of the fixed text. Returns the configured delay. No sending, no credits.

Parameters

NameInTypeReq.Description
campaign_idbodyuuid
agent_idbodyuuid
modebodystringone of: ai, fixed
exact_textbodystringFixed text (≤1500).
samplebodyobject

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/preview/linkedin-followup" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15"}'

Response

json
{
  "data": {
    "mode": "ai",
    "message": "Thanks for connecting, Alex! Quick question — how are you handling onboarding for new accounts today?",
    "followup_delay_minutes": 60
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or agent not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_campaign_linkedin_followup

POST/v1/campaigns/preview/linkedin-invitecampaigns:readnewPreview a LinkedIn invite

Renders the LinkedIn connection note (AI from rules/tone or fixed text with placeholders) and reports its length against the 200-character limit. No sending, no credits.

Parameters

NameInTypeReq.Description
campaign_idbodyuuid
agent_idbodyuuidRequired for AI mode without campaign_id.
modebodystringone of: ai, fixed
promptbodystringInvite rules.
tonebodystringone of: formal, casual, friendly
exact_textbodystringFixed note (≤300).
samplebodyobject

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/preview/linkedin-invite" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15"}'

Response

json
{
  "data": {
    "mode": "ai",
    "message": "Hi Alex, I work with growth teams at B2B SaaS companies in London — would be glad to connect.",
    "charCount": 104,
    "limit": 200,
    "withinLimit": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or agent not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_campaign_linkedin_invite

Emails, replies & delivery 12

GET/v1/campaigns/emails/:emailIdcampaigns:readnewOpen one email

One email event with content, recipient, related open/click/reply events, the reply and the conversation thread. reply is always present (null when the email has no reply).

Parameters

NameInTypeReq.Description
emailIdpathuuidyesEmail event id from sent emails or replies.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/emails/$EMAIL_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (46 lines)
json
{
  "data": {
    "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
    "campaignId": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "companyId": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "eventType": "sent",
    "contactEmail": "james@brightlane.io",
    "subject": "Quicker onboarding at Brightlane",
    "bodyHtml": "<p>Hi James, ...</p>",
    "bodyText": "Hi James, ...",
    "contactName": "James Whitfield",
    "companyName": "Brightlane",
    "companyWebsite": "brightlane.io",
    "sentAt": "2026-10-25T10:03:11.000Z",
    "events": [
      {
        "type": "opened",
        "timestamp": "2026-10-25T12:40:02.000Z"
      },
      {
        "type": "replied",
        "timestamp": "2026-10-26T11:02:00.000Z"
      }
    ],
    "reply": {
      "subject": "Re: Quicker onboarding at Brightlane",
      "body": "Sounds interesting — can you send a short deck?",
      "from": "james@brightlane.io",
      "receivedAt": "2026-10-26T11:02:00.000Z"
    },
    "conversation": [
      {
        "direction": "outbound",
        "subject": "Quicker onboarding at Brightlane",
        "timestamp": "2026-10-25T10:03:11.000Z",
        "type": "initial"
      },
      {
        "direction": "inbound",
        "subject": "Re: Quicker onboarding at Brightlane",
        "timestamp": "2026-10-26T11:02:00.000Z",
        "type": "reply"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundEmail not found or not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_email

POST/v1/campaigns/emails/:emailId/refresh-replycampaigns:writenewReload a reply

Fetches the latest reply text for the email from the sending provider and stores it (use when a reply shows as empty). Throttled to once per 10 s per email.

Parameters

NameInTypeReq.Description
emailIdpathuuidyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/emails/$EMAIL_ID/refresh-reply" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true,
    "reply": {
      "subject": "Re: Quicker onboarding at Brightlane",
      "body": "Sounds interesting — can you send a short deck?",
      "from": "james@brightlane.io",
      "receivedAt": "2026-10-26T11:02:00.000Z"
    }
  }
}

Errors

HTTPCodeWhen
404not_foundEmail not found.
409not_sent_yetCampaign has no provider campaign.
409reply_not_availableNo reply could be loaded.
429rate_limitedToo frequent.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: refresh_campaign_email_reply

POST/v1/campaigns/emails/:emailId/replycampaigns:controlnewconfirmReply to a lead

Sends a manual reply in the lead's thread immediately via the sending provider, bypassing daily limits. REAL email; dry_run returns recipient, subject and the exact text without sending.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
emailIdpathuuidyesThe replied email event id.
bodybodystringyesReply body, HTML allowed (≤50000).
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/emails/$EMAIL_ID/reply" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true, "body": "<p>Hi James, here is the deck: ...</p>"}'

Response

json
{
  "data": {
    "success": true,
    "messageId": "<20261026114512.4471@mail.brightlane-outreach.com>"
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundEmail not found.
409not_sent_yetNo provider campaign to thread into.
502send_failedProvider rejected the reply; nothing was sent.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_reply_to_campaign_email, reply_to_campaign_email

GET/v1/campaigns/:id/email-leadscampaigns:readnewEngaged email leads

Recipients with open/click/reply counts read live from the sending provider. Throttled to one call per 15 s per campaign and key.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
filterquerystringDefault all. one of: opened, clicked, replied, all

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/email-leads" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "leads": [
      {
        "id": "88213456",
        "email": "james@brightlane.io",
        "first_name": "James",
        "last_name": "Whitfield",
        "company_name": "Brightlane",
        "website": "brightlane.io",
        "status": "INPROGRESS",
        "open_count": 3,
        "click_count": 1,
        "reply_count": 1,
        "vendo_company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83"
      }
    ],
    "stats": {
      "total": 1260,
      "opened": 388,
      "clicked": 33,
      "replied": 31
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.
429rate_limitedCalled again within 15 s.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_email_leads

POST/v1/campaigns/:id/emails/preparecampaigns:writenewPre-generate emails (legacy)

Generates emails for every contact ahead of sending. Works only for legacy company-based audiences; people and custom-audience campaigns prepare automatically after launch. Synchronous, can take up to 4 minutes. Nothing is sent.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/prepare" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 120,
    "validated": 118,
    "generated": 116,
    "failed": 4,
    "durationMs": 84211,
    "errors": []
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.
409prepare_not_supportedAudience is not company-based.
504timeoutStill running after 240 s; poll GET /emails/prepared.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: prepare_campaign_emails

GET/v1/campaigns/:id/emails/preparedcampaigns:readnewPrepared emails

Pre-generated emails pipeline with status counts and the preparation progress (pending_validation, generating, ready_to_send, scheduled, queued_in_smartlead, sent, *_failed).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringPipeline status.
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/prepared" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "emails": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "status": "ready_to_send",
        "subject": "Onboarding at Northwind",
        "contact_email": "sara@northwind.dev",
        "created_at": "2026-10-26T08:00:00.000Z"
      }
    ],
    "total": 120,
    "stats": {
      "ready_to_send": 96,
      "scheduled": 20,
      "generation_failed": 4
    },
    "progress": {
      "total": 120,
      "validated": 116,
      "generated": 116,
      "failed": 4,
      "isComplete": true
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_emails

GET/v1/campaigns/:id/emails/queuedcampaigns:readnewQueued emails

Emails scheduled or queued to go out, with counts per state.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
searchquerystring
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/queued" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "emails": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "companyName": "Northwind Labs",
        "contactEmail": "sara@northwind.dev",
        "subject": "Onboarding at Northwind",
        "bodyPreview": "Hi Sara, ...",
        "status": "scheduled",
        "scheduledAt": "2026-10-27T09:20:00.000Z"
      }
    ],
    "total": 44,
    "stats": {
      "scheduled": 40,
      "queued": 4
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_emails

POST/v1/campaigns/:id/emails/retry-preparationcampaigns:writenewRetry failed preparation

Puts prepared emails that failed validation or generation back in the queue (max 3 attempts per email). Nothing is sent.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/retry-preparation" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true,
    "retriedCount": 4
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: retry_campaign_email_preparation

POST/v1/campaigns/:id/emails/schedule-deliverycampaigns:controlnewconfirmSchedule prepared emails

Spreads all ready prepared emails over the campaign's working-hour slots within the daily limit; they are then sent for real automatically. dry_run reports how many are ready.

Credits: Per email sent · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/schedule-delivery" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "scheduled": 96,
    "slots": [
      {
        "date": "2026-10-27",
        "count": 80
      },
      {
        "date": "2026-10-28",
        "count": 16
      }
    ]
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_schedule_campaign_delivery, schedule_campaign_delivery

POST/v1/campaigns/:id/emails/send-nowcampaigns:controlnewconfirmSend prepared emails now

Sends up to limit ready/scheduled prepared emails immediately, outside the schedule. REAL emails. dry_run reports how many would go.

Credits: Per email sent · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.
limitbodyinteger1–100 (default 10).

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/send-now" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true, "limit": 5}'

Response

json
{
  "data": {
    "sent": 5,
    "failed": 0
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_send_campaign_emails_now, send_campaign_emails_now

GET/v1/campaigns/:id/emails/sentcampaigns:readnewSent emails

Emails already sent by the campaign, newest first, filterable by status and searchable by address.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringone of: sent, delivered, opened, clicked, replied, bounced
searchquerystringEmail address contains.
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/emails/sent" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "emails": [
      {
        "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
        "campaignId": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
        "companyId": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "contactEmail": "james@brightlane.io",
        "subject": "Quicker onboarding at Brightlane",
        "bodyPreview": "Hi James, noticed Brightlane...",
        "sentAt": "2026-10-25T10:03:11.000Z",
        "status": "opened",
        "openedAt": "2026-10-25T12:40:02.000Z",
        "clickedAt": null,
        "repliedAt": null,
        "companyName": "Brightlane"
      }
    ],
    "total": 1260
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_emails

GET/v1/campaigns/:id/repliescampaigns:readnewReplies inbox

Email replies of the campaign with body and classification: real, auto_reply (out of office) or bounce. Up to 500, newest first.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
typequerystringone of: real, auto_reply, bounce

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/replies" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
      "received_at": "2026-10-26T11:02:00.000Z",
      "from": "james@brightlane.io",
      "lead_email": "james@brightlane.io",
      "subject": "Re: Quicker onboarding at Brightlane",
      "body": "Sounds interesting — can you send a short deck?",
      "reply_type": "real",
      "company_domain": "brightlane.io"
    }
  ],
  "total": 1
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_replies

AI conversations (threads) 6

GET/v1/campaigns/threads/:threadIdcampaigns:readnewOpen a conversation

One conversation with all AI, human and contact messages. sentiment is always present (null until the thread is classified).

Parameters

NameInTypeReq.Description
threadIdpathuuidyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/threads/$THREAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (32 lines)
json
{
  "data": {
    "id": "7d2e9f1a-4c6b-4a8e-b3d5-1f0c8e2a6b97",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "contact_email": "james@brightlane.io",
    "contact_name": "James Whitfield",
    "status": "ai_handling",
    "sentiment": "positive",
    "message_count": 3,
    "last_message_at": "2026-10-26T11:20:00.000Z",
    "messages": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "direction": "inbound",
        "from": "james@brightlane.io",
        "subject": "Re: Quicker onboarding",
        "body_text": "Can you send a deck?",
        "sent_by": "contact",
        "created_at": "2026-10-26T11:02:00.000Z"
      },
      {
        "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
        "direction": "outbound",
        "subject": "Re: Quicker onboarding",
        "body_text": "Sure — attached. Would Thursday work?",
        "sent_by": "ai",
        "created_at": "2026-10-26T11:20:00.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundThread not found or not the key user's.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_thread

POST/v1/campaigns/threads/:threadId/closecampaigns:controlnewClose a conversation

Marks the conversation closed; the AI no longer replies. Nothing is sent.

Parameters

NameInTypeReq.Description
threadIdpathuuidyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/threads/$THREAD_ID/close" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (32 lines)
json
{
  "data": {
    "id": "7d2e9f1a-4c6b-4a8e-b3d5-1f0c8e2a6b97",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "contact_email": "james@brightlane.io",
    "contact_name": "James Whitfield",
    "status": "closed",
    "sentiment": "positive",
    "message_count": 3,
    "last_message_at": "2026-10-26T11:20:00.000Z",
    "messages": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "direction": "inbound",
        "from": "james@brightlane.io",
        "subject": "Re: Quicker onboarding",
        "body_text": "Can you send a deck?",
        "sent_by": "contact",
        "created_at": "2026-10-26T11:02:00.000Z"
      },
      {
        "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
        "direction": "outbound",
        "subject": "Re: Quicker onboarding",
        "body_text": "Sure — attached. Would Thursday work?",
        "sent_by": "ai",
        "created_at": "2026-10-26T11:20:00.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundThread not found.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: close_campaign_thread

POST/v1/campaigns/threads/:threadId/replycampaigns:controlnewconfirmHuman reply in a conversation

Sends a REAL email to the contact inside the conversation. subject defaults to "Re: <last subject>", body_html defaults to the text as paragraphs. dry_run shows the exact email without sending.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
threadIdpathuuidyes
bodybodystringyesPlain text (≤20000).
subjectbodystring
body_htmlbodystring
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/threads/$THREAD_ID/reply" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true, "body": "Hi James, Thursday 3pm works. Talk then!"}'

Response

Show the full response (32 lines)
json
{
  "data": {
    "id": "7d2e9f1a-4c6b-4a8e-b3d5-1f0c8e2a6b97",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "contact_email": "james@brightlane.io",
    "contact_name": "James Whitfield",
    "status": "ai_handling",
    "sentiment": "positive",
    "message_count": 4,
    "last_message_at": "2026-10-26T11:20:00.000Z",
    "messages": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "direction": "inbound",
        "from": "james@brightlane.io",
        "subject": "Re: Quicker onboarding",
        "body_text": "Can you send a deck?",
        "sent_by": "contact",
        "created_at": "2026-10-26T11:02:00.000Z"
      },
      {
        "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
        "direction": "outbound",
        "subject": "Re: Quicker onboarding",
        "body_text": "Sure — attached. Would Thursday work?",
        "sent_by": "ai",
        "created_at": "2026-10-26T11:20:00.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundThread not found.
502send_failedEmail could not be sent.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_reply_in_campaign_thread, reply_in_campaign_thread

POST/v1/campaigns/threads/:threadId/takeovercampaigns:controlnewTake over a conversation

Stops the AI from replying in this conversation; a human continues. Nothing is sent.

Parameters

NameInTypeReq.Description
threadIdpathuuidyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/threads/$THREAD_ID/takeover" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (32 lines)
json
{
  "data": {
    "id": "7d2e9f1a-4c6b-4a8e-b3d5-1f0c8e2a6b97",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "contact_email": "james@brightlane.io",
    "contact_name": "James Whitfield",
    "status": "human_takeover",
    "sentiment": "positive",
    "message_count": 3,
    "last_message_at": "2026-10-26T11:20:00.000Z",
    "messages": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "direction": "inbound",
        "from": "james@brightlane.io",
        "subject": "Re: Quicker onboarding",
        "body_text": "Can you send a deck?",
        "sent_by": "contact",
        "created_at": "2026-10-26T11:02:00.000Z"
      },
      {
        "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
        "direction": "outbound",
        "subject": "Re: Quicker onboarding",
        "body_text": "Sure — attached. Would Thursday work?",
        "sent_by": "ai",
        "created_at": "2026-10-26T11:20:00.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundThread not found.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: takeover_campaign_thread

GET/v1/campaigns/:id/threadscampaigns:readnewAI email conversations

Conversations the AI handles for the campaign, filterable by status and sentiment. Only threads of the API key's user are visible (same as the app).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringone of: ai_handling, human_takeover, closed
sentimentquerystringone of: positive, objection, referral, unsubscribe, neutral
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/threads" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (37 lines)
json
{
  "data": {
    "threads": [
      {
        "id": "7d2e9f1a-4c6b-4a8e-b3d5-1f0c8e2a6b97",
        "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
        "company_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "contact_email": "james@brightlane.io",
        "contact_name": "James Whitfield",
        "status": "ai_handling",
        "sentiment": "positive",
        "message_count": 3,
        "last_message_at": "2026-10-26T11:20:00.000Z",
        "messages": [
          {
            "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
            "direction": "inbound",
            "from": "james@brightlane.io",
            "subject": "Re: Quicker onboarding",
            "body_text": "Can you send a deck?",
            "sent_by": "contact",
            "created_at": "2026-10-26T11:02:00.000Z"
          },
          {
            "id": "b2f6d8a4-9e1c-4f3b-a7d2-6c0e5b8f1a49",
            "direction": "outbound",
            "subject": "Re: Quicker onboarding",
            "body_text": "Sure — attached. Would Thursday work?",
            "sent_by": "ai",
            "created_at": "2026-10-26T11:20:00.000Z"
          }
        ]
      }
    ],
    "total": 31
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_threads

GET/v1/campaigns/:id/threads/statscampaigns:readnewConversation counters

Counts of the campaign's conversations by status and by sentiment.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/threads/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "byStatus": {
      "ai_handling": 18,
      "human_takeover": 7,
      "closed": 6,
      "total": 31
    },
    "bySentiment": {
      "positive": 12,
      "objection": 9,
      "referral": 2,
      "unsubscribe": 3,
      "neutral": 5
    }
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_threads

LinkedIn accounts, conversations & campaigns 14

GET/v1/campaigns/:id/linkedin/accountscampaigns:readnewAccounts working a campaign

LinkedIn accounts working this campaign with per-account counters.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "accounts": [
      {
        "account": {
          "id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
          "linkedin_name": "Anna Clarke",
          "status": "ok"
        },
        "outreachCount": 206,
        "pending": 94,
        "inviteSent": 71,
        "connected": 25,
        "messageSent": 12,
        "replied": 4
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_linkedin_accounts

POST/v1/campaigns/:id/linkedin/accountscampaigns:controlnewAdd an account to a campaign

Adds a connected account to a running campaign: pending contacts are rebalanced across accounts and backfilled. Invites from it start in the next cycles.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
account_idbodyuuidyesLinkedIn account id.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34"}'

Response

json
{
  "data": {
    "rebalanced": 47,
    "pinned": 0,
    "distribution": {
      "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34": 94
    },
    "alreadyAttached": false,
    "backfillCreated": 12,
    "backfillSkippedAllAccountsTried": 0,
    "backfillSkippedAlreadyConverting": 3,
    "audienceLinkedinTotal": 388
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or account not in the workspace.
409account_not_connectedAccount status is not ok.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: add_campaign_linkedin_account

DELETE/v1/campaigns/:id/linkedin/accounts/:accountIdcampaigns:controlnewRemove an account from a campaign

Stops using the account for this campaign; its pending contacts move to the remaining accounts. Sent invites stay.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
accountIdpathuuidyes

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/accounts/$ACCOUNT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "removed": true,
    "redistributed": 94
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign or account not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: remove_campaign_linkedin_account

POST/v1/campaigns/:id/linkedin/activatecampaigns:controlnewconfirmActivate LinkedIn on a campaign

Queues the campaign audience for LinkedIn on the given accounts (default: settings.selectedLinkedinAccountIds, else all connected) and adds linkedin to the channels. On an active campaign real connection invites start within the next cycles.

Credits: Per invite/message sent · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
account_idsbodyuuid[]Accounts with status ok.
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/activate" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "total": 412,
    "withLinkedin": 388,
    "created": 388,
    "distribution": {
      "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34": 194,
      "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83": 194
    },
    "skippedAlreadyConverting": 0,
    "skippedAllAccountsTried": 0
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or account not in the workspace.
409no_linkedin_accountsNo connected account.
409account_not_connectedA given account is not ok.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_activate_campaign_linkedin, activate_campaign_linkedin

GET/v1/campaigns/:id/linkedin/outreachcampaigns:readnewLinkedIn queue of a campaign

Contacts in the campaign's LinkedIn queue with status, optionally for one account.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringone of: pending, invite_sent, connected, message_sent, replied, failed, skipped
account_idqueryuuid
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/outreach" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "items": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
        "linkedin_account_id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
        "contact_name": "James Whitfield",
        "company_name": "Brightlane",
        "contact_linkedin_url": "https://www.linkedin.com/in/jwhitfield",
        "status": "connected",
        "human_takeover": false,
        "invite_sent_at": "2026-10-24T09:10:00.000Z"
      }
    ],
    "total": 388
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_linkedin_outreach

GET/v1/campaigns/:id/linkedin/statscampaigns:readnewLinkedIn stats of a campaign

Status counters, invites sent today, effective daily limit (min of account warm-up caps and dailyLinkedinLimit) and active accounts.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
account_idqueryuuid

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/linkedin/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 388,
    "pending": 188,
    "inviteSent": 124,
    "connected": 40,
    "messageSent": 25,
    "replied": 11,
    "failed": 0,
    "todayInviteSent": 22,
    "dailyLimit": 40,
    "activeAccounts": 2
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_linkedin_stats

GET/v1/linkedin/accountscampaigns:readupdatedLinkedIn accounts

Connected LinkedIn accounts and their status. Always { role, accounts }. Without detailed: id, linkedin_name, status and the basic account fields; role is null. detailed=true returns the Settings › LinkedIn view: the caller's role, owner, campaigns and SDR searches using each account, 7-day activity, prospecting caps, warm-up stage, premium tier and proxy country (members see only accounts they connected). Fixed (2026-10-26): the plain list was a bare array and exposed the provider account id (unipile_account_id); provider ids are never returned. Example without detailed: {"data": {"role": null, "accounts": [{"id": "e4b8c2d1-…", "linkedin_name": "Anna Clarke", "status": "ok", …}]}}.

Parameters

NameInTypeReq.Description
detailedqueryboolean

Request

curl
curl -s "https://api.vendo-ai.com/v1/linkedin/accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (43 lines)
json
{
  "data": {
    "role": "OWNER",
    "accounts": [
      {
        "id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
        "linkedin_name": "Anna Clarke",
        "status": "ok",
        "proxy_country": "GB",
        "owner_user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "owner_role": "OWNER",
        "is_mine": true,
        "campaigns": [
          {
            "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
            "name": "UK SaaS founders — Q4",
            "status": "active"
          }
        ],
        "sdr_searches": [],
        "activity": {
          "sent_last_7d": 182,
          "last_activity": "2026-10-26T13:20:00.000Z"
        },
        "prospecting": {
          "searches_today": 3,
          "profile_views_today": 41,
          "age_days": 210,
          "warm": true,
          "stage": "full",
          "search_cap": 10,
          "profile_view_cap": 80,
          "invite_cap": 40
        },
        "premium": {
          "tier": "sales_navigator",
          "label": "Sales Navigator"
        },
        "invite_notes_blocked_until": null
      }
    ]
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_linkedin_accounts

POST/v1/linkedin/accounts/connectcampaigns:writeReturns a hosted URL a human must open in a browser to authorise an account. Optional campaignId.

Returns a hosted URL that a HUMAN must open in a browser to authorise a LinkedIn account. This tool cannot complete the connection itself, give the link to the user. No account exists until they finish the flow.

Parameters

NameInTypeReq.Description
campaign_idbodystringOptionally bind the new account to a campaign

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/linkedin/accounts/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_linkedin_account

GET/v1/linkedin/accounts/:accountId/preview-messagescampaigns:readnewSample LinkedIn messages

"Show sample messages": sample invite, follow-up and auto-reply plus suggested pitch, signature and contact info for the account in a campaign, built from the campaign's agent. AI calls on our side; no credits.

Parameters

NameInTypeReq.Description
accountIdpathuuidyes
campaign_idqueryuuidyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/linkedin/accounts/$ACCOUNT_ID/preview-messages?campaign_id=$CAMPAIGN_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "agentName": "Anna",
    "companyName": "Brightlane",
    "companyDescription": "Onboarding automation for B2B SaaS.",
    "sampleInvite": "Hi Alex, ...",
    "sampleFollowup": "Thanks for connecting, Alex! ...",
    "sampleAutoReply": "Great question — ...",
    "suggestedPitch": "We cut onboarding time by 40%.",
    "suggestedInvitePitch": "Onboarding automation for SaaS",
    "suggestedSignature": "Anna\nBrightlane",
    "suggestedContactInfo": "anna@brightlane.io"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundAccount or campaign not in the workspace.
409agent_requiredCampaign has no agent.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_linkedin_account_messages

GET/v1/linkedin/accounts/:accountId/settingscampaigns:readnewLinkedIn account settings

Per-account settings shared by every campaign and SDR process that uses the account: AI auto-replies, invites, follow-ups and signature. The first read creates the defaults, pre-filled from campaign_id's agent.

Parameters

NameInTypeReq.Description
accountIdpathuuidyes
campaign_idqueryuuidPre-fill defaults from this campaign's agent.

Request

curl
curl -s "https://api.vendo-ai.com/v1/linkedin/accounts/$ACCOUNT_ID/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (36 lines)
json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "linkedin_account_id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
    "enabled": true,
    "tone": "friendly",
    "custom_instructions": null,
    "product_pitch": "We cut onboarding time for B2B SaaS by 40%.",
    "calendly_url": "https://calendly.com/anna-clarke/15min",
    "reply_delay_min_minutes": 2,
    "reply_delay_max_minutes": 7,
    "daily_reply_limit": 50,
    "working_hours": {
      "start": 9,
      "end": 18
    },
    "timezone": "Europe/London",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "contact_info": null,
    "invites_enabled": true,
    "invite_tone": "friendly",
    "invite_pitch": "We cut onboarding time for B2B SaaS...",
    "invite_custom_instructions": null,
    "invite_cta": null,
    "followup_enabled": true,
    "followup_pitch": "We cut onboarding time for B2B SaaS by 40%.",
    "followup_custom_instructions": null,
    "followup_cta": "15-minute call this week?",
    "signature_enabled": false,
    "signature_text": "Anna Clarke\nBrightlane",
    "signature_mode": "first_message",
    "created_at": "2026-10-20T09:00:00.000Z",
    "updated_at": "2026-10-26T09:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundAccount or campaign not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_linkedin_account_settings

PATCH/v1/linkedin/accounts/:accountId/settingscampaigns:writenewUpdate LinkedIn account settings

Changes per-account settings (send only changed fields; sending back id/user_id from the GET answers 400 forbidden_field). Applies to all campaigns and SDR processes using the account; to switch AI replies off for one campaign use settings.linkedinAutoReplyDisabled on the campaign.

Parameters

NameInTypeReq.Description
accountIdpathuuidyes
enabledbodybooleanAI auto-replies on/off.
tonebodystringformal|casual|friendly
custom_instructionsbodystring
product_pitchbodystring
calendly_urlbodystringBooking link.
reply_delay_min_minutesbodyinteger0–1440
reply_delay_max_minutesbodyinteger0–1440
daily_reply_limitbodyinteger0–500
working_hoursbodyobject{start, end}
timezonebodystring
agent_idbodyuuid|nullAgent used for replies.
contact_infobodystring
invites_enabledbodyboolean
invite_tonebodystring
invite_pitchbodystring
invite_custom_instructionsbodystring
invite_ctabodystring
followup_enabledbodyboolean
followup_pitchbodystring
followup_custom_instructionsbodystring
followup_ctabodystring
signature_enabledbodyboolean
signature_textbodystring
signature_modebodystringfirst_message|every_message

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/linkedin/accounts/$ACCOUNT_ID/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true,
  "calendly_url": "https://calendly.com/anna-clarke/15min",
  "signature_enabled": true,
  "signature_mode": "first_message"
}'

Response

Show the full response (36 lines)
json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "linkedin_account_id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
    "enabled": true,
    "tone": "friendly",
    "custom_instructions": null,
    "product_pitch": "We cut onboarding time for B2B SaaS by 40%.",
    "calendly_url": "https://calendly.com/anna-clarke/15min",
    "reply_delay_min_minutes": 2,
    "reply_delay_max_minutes": 7,
    "daily_reply_limit": 50,
    "working_hours": {
      "start": 9,
      "end": 18
    },
    "timezone": "Europe/London",
    "agent_id": "a91d4f60-2c3b-4e7a-8f15-0b6c9d2e4a71",
    "contact_info": null,
    "invites_enabled": true,
    "invite_tone": "friendly",
    "invite_pitch": "We cut onboarding time for B2B SaaS...",
    "invite_custom_instructions": null,
    "invite_cta": null,
    "followup_enabled": true,
    "followup_pitch": "We cut onboarding time for B2B SaaS by 40%.",
    "followup_custom_instructions": null,
    "followup_cta": "15-minute call this week?",
    "signature_enabled": true,
    "signature_text": "Anna Clarke\nBrightlane",
    "signature_mode": "first_message",
    "created_at": "2026-10-20T09:00:00.000Z",
    "updated_at": "2026-10-26T09:00:00.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
400forbidden_fieldBody contains id/user_id etc.
404not_foundAccount or agent not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_linkedin_account_settings

DELETE/v1/linkedin/accounts/:idcampaigns:writeupdatedconfirmDisconnect a LinkedIn account

Disconnects the account: every campaign and SDR search sending from it stops immediately and sending capacity drops by roughly 70–80 invites/day. Destructive protocol. Fixed (2026-10-26): a foreign or unknown id answers 404 also for dry_run (it used to preview any id); the preview names the account and where it is used.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesLinkedIn account id from GET /v1/linkedin/accounts
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/linkedin/accounts/$ACCOUNT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Disconnect LinkedIn account \"Anna Clarke\" from this workspace.",
    "account": {
      "id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
      "name": "Anna Clarke",
      "status": "ok"
    },
    "used_by": {
      "campaigns": [
        {
          "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
          "name": "UK SaaS founders — Q4",
          "status": "active"
        }
      ],
      "sdr_campaigns": [],
      "sdr_searches": []
    },
    "warning": "Any campaign or SDR search sending from this account stops immediately. Sending capacity drops by roughly 70–80 invites/day per account removed.",
    "note": "Re-send with { \"confirm\": true } to proceed."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
404not_foundLinkedIn account does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_linkedin_account, disconnect_linkedin_account

POST/v1/linkedin/accounts/:id/reconnectcampaigns:writeupdatedconfirmReconnect a LinkedIn account

Starts re-authentication of an account whose session died: returns a hosted link a human must open. Reconnecting creates a NEW provider account upstream, so per-account history can look empty afterwards and running campaigns may need re-pointing. Destructive protocol. Fixed (2026-10-26): the account must be one of your workspace accounts, a foreign or unknown id answers 404 also for dry_run (it used to preview any id); the preview names the account and the campaigns / SDR searches using it.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesLinkedIn account id from GET /v1/linkedin/accounts
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/linkedin/accounts/$ACCOUNT_ID/reconnect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Start re-authentication for LinkedIn account \"Anna Clarke\".",
    "account": {
      "id": "e4b8c2d1-7a3f-4b9e-8c1d-5f2a6e0b9c34",
      "name": "Anna Clarke",
      "status": "credentials"
    },
    "used_by": {
      "campaigns": [
        {
          "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
          "name": "UK SaaS founders — Q4",
          "status": "active"
        }
      ],
      "sdr_campaigns": [],
      "sdr_searches": []
    },
    "warning": "Reconnecting creates a NEW account id upstream…",
    "note": "Re-send with { \"confirm\": true } to proceed. A human still has to finish the hosted flow in a browser."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
404not_foundLinkedIn account does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_reconnect_linkedin_account, reconnect_linkedin_account

POST/v1/linkedin/outreach/:outreachId/takeovercampaigns:controlnewTake over a LinkedIn contact

Stops the AI from messaging this contact and cancels pending auto-replies. Nothing is sent.

Parameters

NameInTypeReq.Description
outreachIdpathuuidyesOutreach id from the LinkedIn queue or conversations.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/linkedin/outreach/$OUTREACH_ID/takeover" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "contact_name": "James Whitfield",
    "status": "replied",
    "human_takeover": true
  }
}

Errors

HTTPCodeWhen
404not_foundOutreach not found for the key user.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: takeover_linkedin_contact

Mailboxes & senders 5

GET/v1/email/mailboxescampaigns:readnewOwn mailboxes

The workspace's own connected mailboxes with live status (connecting, warming, ready, error, disconnected) and warm-up reputation. Capacity ≈ 50 emails/day per ready box. Used by classic campaigns and SDR in client_owned mode.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/email/mailboxes" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "0f3a6c9d-2e5b-4d8a-b1c4-7e9f2a5d8b16",
      "email": "anna@brightlane-outreach.com",
      "from_name": "Anna Clarke",
      "provider": "google",
      "status": "warming",
      "warmup_reputation": 72,
      "last_error": null,
      "connected_by_user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "created_at": "2026-10-20T09:00:00.000Z",
      "updated_at": "2026-10-26T09:11:00.000Z"
    }
  ],
  "summary": {
    "total": 1,
    "ready": 0,
    "emails_per_day_ready": 0
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_client_mailboxes

POST/v1/email/mailboxescampaigns:writenewConnect an own mailbox

Connects the user's own mailbox with SMTP/IMAP and an app password. The mailbox is registered with the sending operator WITH AUTOMATIC WARM-UP: the real mailbox starts sending and receiving warm-up mail immediately. The password is passed through, never stored by Vendo and never returned; audit logs redact it. Status error means the SMTP/IMAP handshake failed.

Parameters

NameInTypeReq.Description
emailbodystringyes
from_namebodystring
providerbodystringDefault other. one of: google, microsoft, other
smtp_hostbodystringyesBare hostname, e.g. smtp.gmail.com.
smtp_portbodyintegeryese.g. 465 or 587.
imap_hostbodystringyese.g. imap.gmail.com.
imap_portbodyintegeryese.g. 993.
passwordbodystringyesApp password.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/email/mailboxes" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "anna@brightlane-outreach.com",
  "from_name": "Anna Clarke",
  "provider": "google",
  "smtp_host": "smtp.gmail.com",
  "smtp_port": 465,
  "imap_host": "imap.gmail.com",
  "imap_port": 993,
  "password": "abcd efgh ijkl mnop"
}'

Response

json
{
  "data": {
    "id": "0f3a6c9d-2e5b-4d8a-b1c4-7e9f2a5d8b16",
    "email": "anna@brightlane-outreach.com",
    "from_name": "Anna Clarke",
    "provider": "google",
    "status": "warming",
    "warmup_reputation": 0,
    "last_error": null,
    "connected_by_user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "created_at": "2026-10-20T09:00:00.000Z",
    "updated_at": "2026-10-26T09:11:00.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
400invalid_requestMailbox already connected in this workspace, or SMTP/IMAP data missing.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_client_mailbox

DELETE/v1/email/mailboxes/:mailboxIdcampaigns:writenewconfirmDisconnect a mailbox

Stops using the mailbox as a sender (soft disconnect). dry_run lists client_owned campaigns that would be affected and how many usable mailboxes remain; with none left those campaigns cannot send or launch.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
mailboxIdpathuuidyes
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/email/mailboxes/$MAILBOX_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Disconnect mailbox anna@brightlane-outreach.com. It stops being used as a sender immediately.",
    "mailbox": {
      "id": "0f3a6c9d-2e5b-4d8a-b1c4-7e9f2a5d8b16",
      "email": "anna@brightlane-outreach.com",
      "from_name": "Anna Clarke",
      "provider": "google",
      "status": "warming",
      "warmup_reputation": 72,
      "last_error": null,
      "connected_by_user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "created_at": "2026-10-20T09:00:00.000Z",
      "updated_at": "2026-10-26T09:11:00.000Z"
    },
    "remaining_usable_mailboxes": 0,
    "client_owned_campaigns": [
      {
        "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
        "name": "UK SaaS founders — Q4",
        "status": "active"
      }
    ],
    "warning": "No usable own mailbox would remain: client_owned campaigns cannot send and cannot be launched."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
404not_foundMailbox not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_client_mailbox, disconnect_client_mailbox

POST/v1/email/mailboxes/:mailboxId/reconnectcampaigns:writenewReconnect a mailbox

Retries the SMTP/IMAP connection of a mailbox (status error) with the operator and refreshes its status.

Parameters

NameInTypeReq.Description
mailboxIdpathuuidyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/email/mailboxes/$MAILBOX_ID/reconnect" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "0f3a6c9d-2e5b-4d8a-b1c4-7e9f2a5d8b16",
    "email": "anna@brightlane-outreach.com",
    "from_name": "Anna Clarke",
    "provider": "google",
    "status": "ready",
    "warmup_reputation": 88,
    "last_error": null,
    "connected_by_user_id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "created_at": "2026-10-20T09:00:00.000Z",
    "updated_at": "2026-10-26T09:11:00.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundMailbox not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: reconnect_client_mailbox

GET/v1/email/senderscampaigns:readnewSenders and capacity

Sending addresses available to the workspace (Vendo managed pool + own mailboxes) with warm-up level, daily limit and status, plus capacity. Managed senders need a paid plan to launch (summary.paid_plan). Ids go into settings.senderEmailIds. provider_unavailable=true means the provider list could not be loaded (no demo data is returned).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/email/senders" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "senders": [
      {
        "id": "4419021",
        "email": "alex@vendo-mail.io",
        "domain": "vendo-mail.io",
        "warmupLevel": 97,
        "dailyLimit": 40,
        "status": "ready"
      }
    ],
    "summary": {
      "total": 1,
      "ready": 1,
      "emails_per_day_ready": 40,
      "managed_requires_paid_plan": true,
      "paid_plan": true,
      "provider_unavailable": false
    }
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_sender_emails

Phone campaigns 3

POST/v1/campaigns/:id/phone/activatecampaigns:controlnewconfirmActivate AI phone calls

Schedules REAL outbound AI calls to every audience contact with a phone number, using numbers the account already owns (numbers cannot be bought through the API). Saves the phone settings on the campaign. dry_run returns numbers, objective, limits and estimated credits; credits are charged per call minute.

Credits: Per call minute (number rate, ~15/min) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
phone_number_idsbodyuuid[]yesExisting numbers of the account.
call_objectivebodystringDefault inform. one of: sell, inform, book_meeting, collect_info, qualify_lead
talking_pointsbodystring
max_calls_per_daybodyintegerDefault 50.
max_call_duration_secondsbodyintegerDefault 300.
working_hours_startbodyintegerDefault: campaign working hours.
working_hours_endbodyinteger
timezonebodystring
exclude_weekendsbodyboolean
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/phone/activate" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone_number_ids": [
    "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83"
  ],
  "call_objective": "book_meeting",
  "dry_run": true
}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Schedule REAL outbound AI phone calls to every contact with a phone number in \"UK SaaS founders — Q4\" (up to 50/day, 9:00–18:00 Europe/London, weekdays only).",
    "numbers": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "phone_number": "+14155550123",
        "country": "US"
      }
    ],
    "objective": "book_meeting",
    "max_calls_per_day": 50,
    "max_call_duration_seconds": 300,
    "audience_contacts": 412,
    "estimated": {
      "credits_per_minute": 15,
      "max_credits_per_call": 75,
      "max_credits_per_day": 3750
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign or phone number not owned.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_activate_campaign_phone, activate_campaign_phone

GET/v1/campaigns/:id/phone/callscampaigns:readnewCalls of a campaign

Calls with contact, status, outcome, duration, sentiment, summary and recording URL.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringone of: pending, scheduled, calling, completed, failed, skipped, no_answer, voicemail, busy
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/phone/calls" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "items": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "contact_name": "Sara Kim",
        "contact_phone": "+447700900123",
        "company_name": "Northwind Labs",
        "status": "completed",
        "call_outcome": "meeting_booked",
        "call_duration_seconds": 142,
        "call_sentiment": "positive",
        "summary": "Booked a demo for Thursday.",
        "recording_url": "https://recordings.vendo-ai.com/c/91a2.mp3",
        "call_started_at": "2026-10-26T10:01:00.000Z",
        "call_ended_at": "2026-10-26T10:03:22.000Z",
        "created_at": "2026-10-25T18:00:00.000Z"
      }
    ],
    "total": 120
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_phone_calls

GET/v1/campaigns/:id/phone/statscampaigns:readnewPhone stats

Call counters and durations for the campaign.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/phone/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 120,
    "completed": 64,
    "active": 1,
    "pending": 30,
    "no_answer": 18,
    "voicemail": 5,
    "failed": 2,
    "interested": 9,
    "meetings_booked": 4,
    "not_interested": 21,
    "avg_duration_seconds": 96,
    "total_duration_seconds": 6144
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_phone_stats

Monitor, AI lead discovery & live stats 8

GET/v1/campaigns/:id/activitycampaigns:readnewActivity feed

Paged activity events of the campaign (polling replacement for the live stream).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
categoryquerystringone of: discovery, outreach, engagement, system, billing
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/activity" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "events": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "event_type": "email_sent",
        "event_category": "outreach",
        "severity": "info",
        "title": "Email sent to James Whitfield",
        "created_at": "2026-10-26T13:41:09.000Z"
      }
    ],
    "total": 1232
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_activity

GET/v1/campaigns/:id/discoverycampaigns:readnewAI lead discovery config

Discovery settings of a campaign, or null when never configured.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/discovery" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "enabled": true,
    "search_source": "apollo",
    "apollo_search_config": {
      "person_titles": [
        "Head of Growth",
        "VP Marketing"
      ],
      "organization_keywords": [
        "saas"
      ],
      "person_locations": [
        "United Kingdom"
      ]
    },
    "daily_discovery_budget_credits": 50,
    "max_leads_per_day": 25,
    "max_total_discovered": 1000,
    "search_interval_minutes": 60,
    "require_email": true,
    "credits_spent_today": 12,
    "credits_spent_total": 340
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_discovery

PATCH/v1/campaigns/:id/discoverycampaigns:writenewconfirmEnable / change AI lead discovery

Creates or updates automatic lead discovery (Apollo people search or LinkedIn signal search). Whenever the resulting config is enabled it runs daily, SPENDS CREDITS up to the daily budget and grows the campaign audience, so the confirm protocol applies; disabling needs no confirmation.

Credits: Up to daily_discovery_budget_credits per day · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
enabledbodyboolean
search_sourcebodystringone of: apollo, signal
apollo_search_configbodyobject{person_titles[], organization_keywords[], person_locations[]}
signal_search_configbodyobject{queries[], linkedin_account_id}
daily_discovery_budget_creditsbodyintegerMax credits per day.
max_leads_per_daybodyinteger
max_total_discoveredbodyinteger
search_interval_minutesbodyinteger≥5
require_emailbodyboolean
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/discovery" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true,
  "daily_discovery_budget_credits": 50,
  "max_leads_per_day": 25,
  "apollo_search_config": {
    "person_titles": [
      "Head of Growth"
    ],
    "organization_keywords": [
      "saas"
    ],
    "person_locations": [
      "United Kingdom"
    ]
  },
  "dry_run": true
}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Enable AI lead discovery for campaign \"UK SaaS founders — Q4\" — it runs automatically and SPENDS CREDITS every day up to the daily budget, adding new leads to the campaign audience.",
    "enabled": true,
    "source": "apollo",
    "daily_budget_credits": 50,
    "max_leads_per_day": 25,
    "max_total_discovered": null
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.
404not_foundsignal_search_config.linkedin_account_id not in the workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_update_campaign_discovery, update_campaign_discovery

GET/v1/campaigns/:id/discovery/leadscampaigns:readnewDiscovered leads

Leads found by AI discovery with their processing status.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
statusquerystringone of: discovered, enriching, enriched, added_to_audience, rejected, duplicate, enrichment_failed
limitqueryintegerPage size, 1–200 (default 50).
offsetqueryintegerRows to skip (default 0).

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/discovery/leads" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "leads": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "source": "apollo",
        "full_name": "Emma Lowe",
        "title": "Head of Growth",
        "company_name": "Tallyfin",
        "email": "emma@tallyfin.co.uk",
        "status": "added_to_audience",
        "created_at": "2026-10-26T07:12:00.000Z"
      }
    ],
    "total": 410
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_campaign_discovered_leads

GET/v1/campaigns/:id/email-statsanalytics:readnewEmail funnel and delivery

Email funnel of a campaign including the Delivery block: delivered vs not delivered (bounced, rejected as spam, unsubscribed) and the sender inbox-placement test, plus unique opened/clicked/replied recipients. Providers do not report the real inbox vs spam folder of recipients.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/email-stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "sent": 1260,
    "delivered": 1251,
    "opened": 512,
    "clicked": 41,
    "replied": 37,
    "bounced": 9,
    "repliedReal": 29,
    "repliedAutoReply": 6,
    "repliedBounce": 2,
    "delivery": {
      "sent": 1260,
      "delivered": 1251,
      "bounced": 9,
      "rejected_spam": 1,
      "unsubscribed": 3,
      "inbox_placement": {
        "tested_at": "2026-10-24T08:00:00.000Z",
        "inbox_pct": 92,
        "spam_pct": 8
      }
    },
    "unique": {
      "opened": 388,
      "clicked": 33,
      "replied": 31
    }
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_email_stats

GET/v1/campaigns/:id/live-statusanalytics:readnewEmail progress

Email channel progress for the campaign page: audience size, processed contacts, percent complete, event counts, queue depth and the provider's status and sends today.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/live-status" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "totalInAudience": 412,
    "totalProcessed": 318,
    "percentComplete": 77,
    "eventCounts": {
      "sent": 318,
      "skipped": 6,
      "bounced": 4,
      "opened": 141,
      "clicked": 12,
      "replied": 9
    },
    "queueDepth": 44,
    "queueInFlight": 3,
    "smartleadStatus": "ACTIVE",
    "smartleadSentToday": 61
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_live_status

GET/v1/campaigns/:id/monitoranalytics:readnewCampaign monitor

Everything the Monitor page shows: discovery config and stats, audience growth by day, the last 100 activity events, event counts and credits (balance, spent today/total, daily budget).

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/monitor" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (62 lines)
json
{
  "data": {
    "discoveryConfig": {
      "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
      "enabled": true,
      "search_source": "apollo",
      "apollo_search_config": {
        "person_titles": [
          "Head of Growth",
          "VP Marketing"
        ],
        "organization_keywords": [
          "saas"
        ],
        "person_locations": [
          "United Kingdom"
        ]
      },
      "daily_discovery_budget_credits": 50,
      "max_leads_per_day": 25,
      "max_total_discovered": 1000,
      "search_interval_minutes": 60,
      "require_email": true,
      "credits_spent_today": 12,
      "credits_spent_total": 340
    },
    "discoveryStats": {
      "discovered": 410,
      "enriched": 352,
      "added_to_audience": 301
    },
    "audienceGrowth": [
      {
        "date": "2026-10-25",
        "total": 380,
        "newLeads": 22
      }
    ],
    "recentActivity": [
      {
        "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
        "event_type": "lead_added",
        "event_category": "discovery",
        "severity": "success",
        "title": "3 new leads added",
        "created_at": "2026-10-26T10:00:00.000Z"
      }
    ],
    "eventCounts": {
      "discovery": 120,
      "outreach": 900,
      "engagement": 210
    },
    "financials": {
      "creditsBalance": 1840,
      "creditsSpentToday": 12,
      "creditsSpentTotal": 340,
      "dailyBudget": 50
    }
  }
}

Errors

HTTPCodeWhen
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_monitor

GET/v1/campaigns/:id/pulseanalytics:readnewCampaign pulse

Dashboard pulse: what the campaign is doing now, today vs daily limits, a 7–28-day email and LinkedIn series and the latest events. Database only, fast.

Parameters

NameInTypeReq.Description
idpathuuidyesCampaign id.
daysqueryintegerSeries length 7–28 (default 14).
limitqueryintegerRecent events 8–60.

Request

curl
curl -s "https://api.vendo-ai.com/v1/campaigns/$CAMPAIGN_ID/pulse" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (89 lines)
json
{
  "data": {
    "campaign_id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
    "status": "active",
    "channels": [
      "email",
      "linkedin"
    ],
    "email": {
      "sent": 1260,
      "opened": 512,
      "clicked": 41,
      "replied": 37,
      "bounced": 9,
      "queued": 44,
      "failed": 2,
      "today": 61,
      "daily_limit": 80,
      "daily": [
        52,
        80,
        79,
        0,
        0,
        74,
        61
      ],
      "daily_replies": [
        2,
        4,
        3,
        0,
        0,
        5,
        1
      ],
      "last_sent_at": "2026-10-26T13:41:09.000Z"
    },
    "linkedin": {
      "queued": 188,
      "invites": 214,
      "connected": 63,
      "messages": 58,
      "replied": 11,
      "today": 22,
      "daily_limit": 40,
      "daily": [
        30,
        38,
        40,
        0,
        0,
        35,
        22
      ],
      "daily_connected": [
        6,
        9,
        8,
        2,
        1,
        7,
        4
      ],
      "accounts": 2,
      "last_invite_at": "2026-10-26T13:20:00.000Z",
      "last_reply_at": "2026-10-26T11:02:00.000Z"
    },
    "now": {
      "state": "sending",
      "line": "Sending: 61 of 80 emails today",
      "last_event_at": "2026-10-26T13:41:09.000Z"
    },
    "recent": [
      {
        "at": "2026-10-26T13:41:09.000Z",
        "channel": "email",
        "kind": "sent",
        "who": "James Whitfield",
        "company": "Brightlane",
        "avatar_url": null
      }
    ],
    "days": [
      "2026-10-20",
      "2026-10-26"
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.
404not_foundCampaign does not exist or belongs to another workspace.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_pulse

Content (social) accounts 3

GET/v1/content/accountsworkspace:readnewSocial publishing accounts

LinkedIn and Instagram accounts connected for publishing posts (Settings › Social). Accounts of the key's user.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/content/accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "5f1c2a9e-8b7d-4c3e-9a61-2d4f7b0c1e83",
      "provider": "LINKEDIN",
      "status": "connected",
      "display_name": "Anna Clarke",
      "username": "anna-clarke",
      "avatar_url": "https://media.licdn.com/a.jpg",
      "profile_url": "https://www.linkedin.com/in/anna-clarke",
      "created_at": "2026-10-10T08:00:00.000Z"
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_content_accounts

POST/v1/content/accounts/connectworkspace:writenewConnect a publishing account

Returns a hosted URL that a human must open to authorise a LinkedIn or Instagram publishing account; nothing is connected until they finish.

Parameters

NameInTypeReq.Description
providerbodystringyesone of: LINKEDIN, INSTAGRAM
redirect_pathbodystringApp path to return to (default /settings?tab=social).

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/content/accounts/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider": "INSTAGRAM"}'

Response

json
{
  "data": {
    "url": "https://account.unipile.com/hosted/abc123"
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_content_account

DELETE/v1/content/accounts/:accountIdworkspace:writenewconfirmDisconnect a publishing account

Removes the publishing account; it can no longer publish posts.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
accountIdpathuuidyes
dry_runbodybooleantrue = preview only, nothing happens.
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required.

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/content/accounts/$ACCOUNT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "ok": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent.
404not_foundAccount not found.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_content_account, disconnect_content_account

Audiences API 26

Search the 4M+ People database, save audiences, manage uploaded custom audiences with enrichment, and buy ready-made Audience Sets.

Custom audiences (My Audiences) 16

GET/v1/audiences/customaudiences:readnewList custom audiences

Lists the workspace's custom audiences ("My Audiences": uploaded files, purchased sets, CRM selections) with status, contact counts and enrichment progress. These are not campaign audiences (GET /v1/audiences). Without limit the whole list is returned.

Parameters

NameInTypeReq.Description
pagequeryintegerPage (1-based), used with limit
limitqueryinteger1..100; omit for all

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/custom" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
      "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "name": "UK fintech CFOs",
      "description": null,
      "status": "ready",
      "source_file_name": "uk-fintech-cfos.csv",
      "total_contacts": 412,
      "with_email": 388,
      "with_phone": 97,
      "with_linkedin": 405,
      "processing_errors": null,
      "tags": [],
      "column_mapping": {
        "0": "first_name",
        "1": "last_name",
        "2": "email",
        "3": "company_name"
      },
      "enrichment_progress": 0,
      "enrichment_total": 0,
      "enrichment_done": 0,
      "created_at": "2026-10-26T09:14:22.000Z",
      "updated_at": "2026-10-26T09:14:22.000Z"
    }
  ]
}

Errors

HTTPCodeWhen
400invalid_requestpage/limit out of range

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_custom_audiences

POST/v1/audiences/customaudiences:writenewCreate an empty custom audience

Creates an empty, named custom audience (status ready), the CRM "Save to new audience" action. Fill it with POST /v1/audiences/custom/{id}/contacts. No credits.

Parameters

NameInTypeReq.Description
namebodystringyesAudience name, 1..255 chars

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Webinar sign-ups Oct"}'

Response

json
{
  "data": {
    "success": true,
    "audience": {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
      "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "name": "Webinar sign-ups Oct",
      "description": null,
      "status": "ready",
      "source_file_name": null,
      "total_contacts": 0,
      "with_email": 0,
      "with_phone": 0,
      "with_linkedin": 0,
      "processing_errors": null,
      "tags": [],
      "column_mapping": null,
      "enrichment_progress": 0,
      "enrichment_total": 0,
      "enrichment_done": 0,
      "created_at": "2026-10-26T09:14:22.000Z",
      "updated_at": "2026-10-26T09:14:22.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestname missing or too long

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_custom_audience

POST/v1/audiences/custom/previewaudiences:writenewPreview a contacts file (column mapping)

Step 1 of an upload: detects the columns of a CSV/XLSX/XLS/TSV/TXT file and suggests a mapping to contact fields, with sample rows. Creates nothing and spends nothing. Accepts multipart/form-data (field "file", ≤50 MB) or JSON (content_base64 + filename, or file_url).

Parameters

NameInTypeReq.Description
filebodyfile (multipart)The file, as multipart/form-data field "file". Alternatively send JSON with content_base64 + filename, or file_url
filenamebodystringJSON variant: file name with extension (.csv, .xlsx, .xls, .tsv, .txt); required with content_base64
content_base64bodystringJSON variant: file content as base64 (request body ≤10 MB, i.e. ≈7 MB file)
file_urlbodystringJSON variant: public http(s) URL fetched server-side (SSRF-protected, ≤10 MB)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/preview" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "filename": "leads.csv",
  "content_base64": "Zmlyc3RfbmFtZSxsYXN0X25hbWUsZW1haWwKQW1lbGlhLEhhcnQsYW1lbGlhQGxlZGdlcmx5LmNvLnVr"
}'

Response

Show the full response (36 lines)
json
{
  "data": {
    "success": true,
    "headers": [
      "first_name",
      "last_name",
      "email"
    ],
    "suggestedMapping": {
      "0": "first_name",
      "1": "last_name",
      "2": "email"
    },
    "sampleRows": [
      [
        "Amelia",
        "Hart",
        "amelia@ledgerly.co.uk"
      ]
    ],
    "totalRows": 1,
    "delimiter": ",",
    "fieldOptions": [
      {
        "value": "first_name",
        "label": "First name",
        "core": true
      },
      {
        "value": "email",
        "label": "Email",
        "core": true
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestNo file, unsupported extension or empty base64
400forbidden_urlfile_url points to a private network address
413payload_too_largeBody larger than the limit

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_custom_audience_file

POST/v1/audiences/custom/uploadaudiences:writenewCreate a custom audience from a file

Creates a custom audience from a CSV/XLSX/XLS/TSV/TXT file; parsing runs in the background, so poll GET /v1/audiences/custom/{id} until status is "ready" (or "error"). Optional column_mapping (from the preview) overrides auto-detection. No credits, enrichment is a separate paid step.

Parameters

NameInTypeReq.Description
filebodyfile (multipart)The file, as multipart/form-data field "file". Alternatively send JSON with content_base64 + filename, or file_url
filenamebodystringJSON variant: file name with extension (.csv, .xlsx, .xls, .tsv, .txt); required with content_base64
content_base64bodystringJSON variant: file content as base64 (request body ≤10 MB, i.e. ≈7 MB file)
file_urlbodystringJSON variant: public http(s) URL fetched server-side (SSRF-protected, ≤10 MB)
namebodystringAudience name (default: file name)
column_mappingbodyobject{ "<column index>": "<field>" }; JSON string in multipart

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/upload" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file_url": "https://example.com/exports/uk-fintech-cfos.csv",
  "name": "UK fintech CFOs",
  "column_mapping": {
    "0": "first_name",
    "1": "last_name",
    "2": "email",
    "3": "company_name"
  }
}'

Response

json
{
  "data": {
    "success": true,
    "audience": {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "name": "UK fintech CFOs",
      "status": "processing",
      "source_file_name": "uk-fintech-cfos.csv"
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestNo file, unsupported type or malformed column_mapping
400fetch_failedfile_url could not be downloaded

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: upload_custom_audience

GET/v1/audiences/custom/:idaudiences:readnewGet a custom audience

One custom audience with live counters, enrichment progress, column mapping and the first 20 contacts. Use the contacts route to page through all of them.

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (48 lines)
json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
    "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "name": "UK fintech CFOs",
    "description": null,
    "status": "ready",
    "source_file_name": "uk-fintech-cfos.csv",
    "total_contacts": 412,
    "with_email": 388,
    "with_phone": 97,
    "with_linkedin": 405,
    "processing_errors": null,
    "tags": [],
    "column_mapping": {
      "0": "first_name",
      "1": "last_name",
      "2": "email",
      "3": "company_name"
    },
    "enrichment_progress": 0,
    "enrichment_total": 0,
    "enrichment_done": 0,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z",
    "contacts_preview": [
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "audience_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "first_name": "Amelia",
        "last_name": "Hart",
        "full_name": "Amelia Hart",
        "email": "amelia.hart@ledgerly.co.uk",
        "phone": null,
        "linkedin_url": "https://www.linkedin.com/in/ameliahart",
        "company_name": "Ledgerly",
        "position": "CFO",
        "country": "United Kingdom",
        "city": "London",
        "enriched": false,
        "signal_context": null,
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ],
    "contacts_note": "First 20 contacts only — page through GET /v1/audiences/custom/{id}/contacts for all of them."
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
400invalid_requestid is not a UUID

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_custom_audience

PATCH/v1/audiences/custom/:idaudiences:writenewRename a custom audience

Renames a custom audience. Reversible, no credits.

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
namebodystringyesNew name, 1..255 chars

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "UK fintech CFOs — Q4"}'

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
    "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "name": "UK fintech CFOs — Q4",
    "description": null,
    "status": "ready",
    "source_file_name": "uk-fintech-cfos.csv",
    "total_contacts": 412,
    "with_email": 388,
    "with_phone": 97,
    "with_linkedin": 405,
    "processing_errors": null,
    "tags": [],
    "column_mapping": {
      "0": "first_name",
      "1": "last_name",
      "2": "email",
      "3": "company_name"
    },
    "enrichment_progress": 0,
    "enrichment_total": 0,
    "enrichment_done": 0,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
400invalid_requestname missing

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: rename_custom_audience

DELETE/v1/audiences/custom/:idaudiences:writenewconfirmDelete a custom audience

Permanently deletes the audience, all its contacts and the source file. dry_run returns the name and contact count; confirm deletes. Campaign audiences already built from it are not removed.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Permanently delete the audience \"UK fintech CFOs\" with all 412 contacts and its source file. Campaign audiences already created from it keep working until their campaigns finish. Cannot be undone.",
    "estimated": {
      "audience": {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "name": "UK fintech CFOs",
        "status": "ready",
        "total_contacts": 412
      }
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundAudience does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_custom_audience, delete_custom_audience

GET/v1/audiences/custom/:id/contactsaudiences:readnewList contacts of a custom audience

Paged contacts (max 100 per page). Contacts added by Vendo SDR carry signal_context explaining why they were picked (reasoning, relevance score, ICP group).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
pagequeryintegerDefault 1
limitqueryinteger1..100, default 50

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/contacts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "contacts": [
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "audience_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "first_name": "Amelia",
        "last_name": "Hart",
        "full_name": "Amelia Hart",
        "email": "amelia.hart@ledgerly.co.uk",
        "phone": null,
        "linkedin_url": "https://www.linkedin.com/in/ameliahart",
        "company_name": "Ledgerly",
        "position": "CFO",
        "country": "United Kingdom",
        "city": "London",
        "enriched": false,
        "signal_context": null,
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ],
    "total": 412,
    "page": 1,
    "totalPages": 9
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
400invalid_requestpage/limit out of range

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_custom_audience_contacts

POST/v1/audiences/custom/:id/contactsaudiences:writenewAdd contacts to a custom audience

Appends up to 5000 contacts per call. Deduplicated inside the audience by email, LinkedIn URL or phone; duplicates and contacts with no way to reach them are skipped (not overwritten). No credits.

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
contactsbodyarrayyes[{ first_name, last_name, email, phone, linkedin_url, company_name, position }], 1..5000

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/contacts" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "contacts": [
    {
      "first_name": "Tom",
      "last_name": "Reyes",
      "email": "tom@brightpay.io",
      "company_name": "BrightPay",
      "position": "Head of Finance"
    }
  ]
}'

Response

json
{
  "data": {
    "success": true,
    "added": 1,
    "skipped": 0,
    "total_contacts": 413
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
400invalid_requestcontacts empty or more than 5000

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: add_custom_audience_contacts

PATCH/v1/audiences/custom/:id/contacts/:contactIdaudiences:writenewEdit a contact

Updates the given fields of one contact (first_name, last_name, full_name, email, phone, linkedin_url, company_name, position, country, city, website, whatsapp, telegram, facebook_url, instagram_url). Other fields are ignored. The response returns the updated contact row without signal_context (read it with GET …/contacts).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
contactIdpathuuidyesContact id
emailbodystringAny editable field, see description

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/contacts/$CONTACT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "amelia.hart@ledgerly.com", "position": "Chief Financial Officer"}'

Response

json
{
  "data": {
    "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "audience_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "first_name": "Amelia",
    "last_name": "Hart",
    "full_name": "Amelia Hart",
    "email": "amelia.hart@ledgerly.com",
    "phone": null,
    "linkedin_url": "https://www.linkedin.com/in/ameliahart",
    "company_name": "Ledgerly",
    "position": "Chief Financial Officer",
    "country": "United Kingdom",
    "city": "London",
    "enriched": false,
    "created_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
404not_foundAudience or contact does not exist in this workspace
400invalid_requestNo editable field given

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_custom_audience_contact

DELETE/v1/audiences/custom/:id/contacts/:contactIdaudiences:writenewconfirmRemove a contact

Permanently removes one contact from the audience. The optional reason (UI: Not relevant / Wrong contact / Already a customer / Bad data / Other) is logged to improve automatic selection.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
contactIdpathuuidyesContact id
reasonbodystringWhy it is removed (≤200 chars)
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/contacts/$CONTACT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true, "reason": "Already a customer"}'

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundAudience or contact does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_custom_audience_contact, delete_custom_audience_contact

POST/v1/audiences/custom/:id/enrichaudiences:writenewconfirmEnrich a custom audience

Starts background enrichment (email, phone, LinkedIn, WhatsApp lookups) of the not-yet-enriched contacts. dry_run returns how many contacts would be processed, the maximum cost, estimated minutes and the balance; confirm starts it. Credits are charged per contact actually enriched while it runs; it needs at least 1 credit to start. Poll GET …/enrichment.

Credits: 1 credit per successfully enriched contact (max = to_enrich) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/enrich" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Enrich 380 not-yet-enriched contacts of \"UK fintech CFOs\" (email, phone, LinkedIn, WhatsApp lookups). Runs in the background (~26 min); poll GET /v1/audiences/custom/3f9c…/enrichment. Charged 1 credit per contact that is actually enriched — at most the estimate. Needs at least 1 credit to start; stops when credits run out.",
    "estimated": {
      "to_enrich": 380,
      "max_credits": 380,
      "estimated_minutes": 26,
      "balance": 2140,
      "can_start": true,
      "current_status": "ready"
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundAudience does not exist in this workspace
402insufficient_creditsBalance below 1 credit
409conflictAlready enriching, or nothing left to enrich

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_enrich_custom_audience, enrich_custom_audience

POST/v1/audiences/custom/:id/enrich/stopaudiences:writenewStop enrichment

Stops a running enrichment after the current contact. Contacts already enriched stay enriched (and charged).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/enrich/stop" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true,
    "message": "Stop requested — will halt after current contact"
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
409conflictEnrichment is not running

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: stop_custom_audience_enrichment

GET/v1/audiences/custom/:id/enrichmentaudiences:readnewEnrichment progress

Polling replacement for the app's live progress stream: status, totals, done count and percentage. done=true once the audience is no longer enriching/processing.

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/enrichment" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "name": "UK fintech CFOs",
    "status": "enriching",
    "enrichment_total": 380,
    "enrichment_done": 112,
    "enrichment_progress": 29,
    "total_contacts": 412,
    "with_email": 388,
    "with_phone": 131,
    "with_linkedin": 405,
    "done": false
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_custom_audience_enrichment

GET/v1/audiences/custom/:id/export.csvaudiences:readnewExport a custom audience as CSV

Downloads every contact as CSV (Content-Disposition filename, X-Total-Rows header). With ?format=json the CSV is wrapped as { filename, total_rows, csv } (used by MCP).

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
formatquerystring"json" to get a JSON envelope one of: json

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/export.csv" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "filename": "uk-fintech-cfos.csv",
    "total_rows": 412,
    "csv": "first_name,last_name,email,phone,linkedin_url,company_name,position\nAmelia,Hart,amelia.hart@ledgerly.co.uk,,https://www.linkedin.com/in/ameliahart,Ledgerly,CFO\n"
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: export_custom_audience_csv

POST/v1/audiences/custom/:id/to-campaign-audienceaudiences:writenewUse a custom audience in a campaign

The "Launch campaign" button of My Audiences: wraps a READY custom audience as a campaign audience and returns its id for POST /v1/campaigns (audienceId). Contacts stay in the custom audience; nothing is sent and no credits are spent.

Parameters

NameInTypeReq.Description
idpathuuidyesCustom audience id
namebodystringCampaign audience name

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/custom/$AUDIENCE_ID/to-campaign-audience" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "UK fintech CFOs — Nov campaign"}'

Response

json
{
  "data": {
    "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "name": "UK fintech CFOs — Nov campaign",
    "status": "ready",
    "stats": {
      "totalContacts": 412,
      "withEmail": 388,
      "withPhone": 131,
      "withLinkedin": 405
    }
  }
}

Errors

HTTPCodeWhen
404not_foundAudience does not exist in this workspace
409conflictAudience is still processing/enriching or in error

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: prepare_campaign_audience_from_custom

Audience Sets marketplace 6

GET/v1/audiences/setsaudiences:readBrowse the marketplace catalog and its categories. Free.

Browse pre-built, ready-to-use audiences available for purchase, plus their categories. Browsing is free; buying costs credits. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/sets" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_audience_sets

POST/v1/audiences/sets/:id/buyaudiences:writeupdatedconfirmBuy an audience set

Purchases a marketplace set and snapshots its contacts into My Audiences; credits are deducted immediately. dry_run now returns the set, contact count, price per contact, estimated credits, balance and can_afford.

Credits: price_per_contact × contacts (usually 2 per contact) · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesSet id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/sets/$SET_ID/buy" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Buy \"UK fintech founders\" and snapshot about 640 contacts into My Audiences (GET /v1/audiences/custom).",
    "estimated": {
      "set": {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "slug": "uk-fintech-founders",
        "title": "UK fintech founders",
        "source": "people",
        "est_count": 640,
        "price_per_contact": 2,
        "purchased": false
      },
      "contact_count": 640,
      "price_per_contact": 2,
      "estimated_credits": 1280,
      "balance": 2140,
      "can_afford": true
    },
    "cost": "2 credits per contact; the exact count is fixed at purchase time (est. 1280 credits).",
    "note": "Re-send with { \"confirm\": true } to execute. Credits are deducted immediately."
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundUnknown set id
400invalid_requestInsufficient credits or set unavailable

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_buy_audience_set, buy_audience_set

POST/v1/audiences/sets/:id/launchaudiences:writeupdatedconfirmPrepare a campaign audience from a set

Creates a campaign-ready audience from a set and returns its audienceId, it does NOT contact anyone (the old description wrongly said it started outreach). Signals sets buy a fresh snapshot first (price per contact, also added to My Audiences); people sets are free. Next: POST /v1/campaigns with audienceId, then preview and launch. Scope is now audiences:write only.

Credits: signals sets: price_per_contact × contacts; people sets: free · Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesSet id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/audiences/sets/$SET_ID/launch" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "audienceId": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "name": "UK fintech founders",
    "contactCount": 612
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundUnknown set id
400invalid_requestInsufficient credits or set cannot be launched

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_launch_audience_set, launch_audience_set

GET/v1/audiences/sets/:slugaudiences:readDetails of one set: size, description, freshness. slug is human-readable (e.g. uk-fintech-founders), not a UUID.

Details of one marketplace set by slug: size, description, freshness. Use the contact count here to work out the purchase cost (2 credits per contact). Read-only, free.

Parameters

NameInTypeReq.Description
slugpathstringyesHuman-readable slug, not a UUID

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/sets/$SET_SLUG" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_audience_set

GET/v1/audiences/sets/:slug/contactsaudiences:readPaginated contact preview. Accepts offset and limit. Contact details stay masked until purchase.

Paginated preview of the contacts inside a marketplace set. Contact details stay masked until the set is purchased. Read-only, free.

Parameters

NameInTypeReq.Description
slugpathstringyes
offsetquerynumber
limitquerynumber

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/sets/$SET_SLUG/contacts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_audience_set_contacts

GET/v1/audiences/sets/:slug/contacts/:contactIdaudiences:readupdatedOne contact of an audience set

Full card of one contact in a marketplace set: person, company, buying signal and channels. Contact details stay masked until the set is purchased. Free. Response shape (docs fixed 2026-10-26): id, locked, source, person {full_name, title, …}, company, signal and channels {email (masked until unlocked), has_email, linkedin_url (null until unlocked), phone (masked), …}.

Parameters

NameInTypeReq.Description
slugpathstringyesSet slug
contactIdpathstringyesContact id from GET /v1/audiences/sets/{slug}/contacts

Request

curl
curl -s "https://api.vendo-ai.com/v1/audiences/sets/$SET_SLUG/contacts/$CONTACT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (31 lines)
json
{
  "data": {
    "id": "7d2a9f4c-1b3e-4c8a-9e6d-0f5b2a8c4e19",
    "locked": true,
    "source": "linkedin_jobs",
    "person": {
      "full_name": "Sarah Mitchell",
      "title": "Practice Manager",
      "seniority": "manager",
      "location": "Leeds, United Kingdom"
    },
    "company": {
      "name": "Northgate Dental",
      "domain": "northgatedental.co.uk",
      "industry": "Dental clinics",
      "size": "11-50"
    },
    "signal": {
      "type": "hiring",
      "summary": "Hiring a dental receptionist",
      "detected_at": "2026-10-24T08:12:00.000Z"
    },
    "channels": {
      "email": "s•••@northgatedental.co.uk",
      "has_email": true,
      "linkedin_url": null,
      "phone": "•••",
      "has_phone": true
    }
  }
}

Errors

HTTPCodeWhen
404not_foundUnknown slug or contact not in the set
400invalid_requestMalformed slug or contactId

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_audience_set_contact

CRM API 48

The built-in Vendo CRM: leads, pipelines (branches) and stages, documents, the communications stream, calendar, notifications and settings.

Leads 16

GET/v1/crm/leadscrm:readList pipeline leads. Query: stage, source, q, range (24h…1y|all), limit (≤200), offset

List leads in the CRM pipeline. Filters: branch ("general" or a branch id from crm_list_branches; omitted = all branches visible to the user), stage (stage id), owner (user id from crm_list_workspace_members), source, q (name/company/email search), range or from/to, limit/offset. Read-only. Great first call for "who replied to me?".

Parameters

NameInTypeReq.Description
branchquerystring
stagequerystring
ownerquerystringAssigned user id
sourcequerystringone of: linkedin_outreach_reply, linkedin_inbound, linkedin_manual, email_reply_real, widget_chat, widget_voice, widget_form, narrator_form, telegram_message, whatsapp_message, phone_call_interested, meeting_booked, crm_integration_inbound, manual
qquerystring
rangequerystringOmit for all time one of: 24h, 7d, 30d, 90d, 180d, 1y
fromquerystringISO date, created from
toquerystringISO date, created to
limitqueryintegermin 1; max 200
offsetqueryintegermin 0

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_leads

POST/v1/crm/leadscrm:writeupdatedCreate a lead

Adds a lead card (all fields optional, at least one identity field). Now verifies that assigned_user_id is a member of this workspace and that branch_id / branch_ids are pipelines of this workspace before creating.

Parameters

NameInTypeReq.Description
first_namebodystring
last_namebodystring
company_namebodystring
titlebodystring
emailbodystring
linkedin_urlbodystringhttp(s) URL
phonebodystring
avatar_urlbodystring
instagram_urlbodystring
facebook_urlbodystring
assigned_user_idbodyuuidOwner, a workspace member
notesbodystring≤8000
branch_idsbodystring[]"general" and/or branch ids

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "first_name": "Tom",
  "last_name": "Reyes",
  "company_name": "BrightPay",
  "email": "tom@brightpay.io",
  "branch_ids": [
    "general",
    "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d"
  ]
}'

Response

json
{
  "data": {
    "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "first_name": "Tom",
    "last_name": "Reyes",
    "company_name": "BrightPay",
    "email": "tom@brightpay.io",
    "source": "manual",
    "stage_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "assigned_user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
    "created_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestEmpty card, invalid field, foreign assignee or unknown branch

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_create_lead

POST/v1/crm/leads/enrichcrm:writeEnrich from LinkedIn URL → name, title, employer, avatar, location, and unverified pattern-guess email. Consumes 1 LinkedIn budget

Enrich from LinkedIn URL (same Unipile lookup the SDR hunter uses; consumes the workspace's shared daily profile-view budget). Fetch name / title / current employer / avatar / location for a linkedin.com/in/... URL (same lookup the SDR hunters use). May also suggest an email as an UNVERIFIED pattern guess (email_confidence=pattern_guess). Costs one profile view from the workspace's shared daily LinkedIn budget, do not loop over big lists.

Parameters

NameInTypeReq.Description
linkedin_urlbodystringyes

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/enrich" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"linkedin_url": "<linkedin_url>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_enrich_from_linkedin

POST/v1/crm/leads/importcrm:writenewImport leads

Imports up to 1000 already-parsed rows into the chosen pipelines (entry stage). Deduplicated by email/phone: an existing lead only gets blank fields filled (never overwritten) and is added to the pipelines. Returns a summary with skipped rows and reasons.

Parameters

NameInTypeReq.Description
rowsbodyarrayyes[{ first_name, last_name, email, phone, company, title, linkedin, instagram, facebook }], 1..1000
branch_idsbodystring[]"general" and/or branch ids (default General)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/import" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rows": [
    {
      "first_name": "Tom",
      "last_name": "Reyes",
      "email": "tom@brightpay.io",
      "company": "BrightPay",
      "title": "Head of Finance"
    }
  ],
  "branch_ids": [
    "general"
  ]
}'

Response

json
{
  "data": {
    "created": 1,
    "updated": 0,
    "skipped": 0,
    "skippedRows": []
  }
}

Errors

HTTPCodeWhen
400invalid_requestrows empty, more than 1000, or unknown branch

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_import_leads

GET/v1/crm/leads/:idcrm:readFull lead card: identity, origin, company enrichment, activity, comments

Full lead card: identity, source ("why is this person here"), company enrichment, activities, comments, assignments. Read-only.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_lead

PATCH/v1/crm/leads/:idcrm:writeUpdate lead attributes

Patch lead attributes (name, company, title, email, LinkedIn, phone, notes, tags, deal value + currency). Stage and owner have their own tools (crm_move_lead, reassign_crm_lead). Reversible.

Parameters

NameInTypeReq.Description
idpathstringyes
first_namebodystring
last_namebodystring
company_namebodystring
titlebodystring
emailbodystring
linkedin_urlbodystringFull http(s) URL
phonebodystring
notesbodystring
tagsbodystring[]Replaces the tag list
deal_valuebodynumber
currencybodystringISO code, e.g. USD max length 3

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_update_lead

DELETE/v1/crm/leads/:idcrm:writeconfirmSoft-delete a lead (requires confirm)

Destructive : soft-delete a lead. Soft-delete a lead, it disappears from the pipeline (restorable by support only). Only call after the user reviewed preview_delete_crm_lead output and explicitly approved.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathstringyes
dry_runbodybooleantrue = preview only (nothing happens)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_crm_lead, delete_crm_lead

GET/v1/crm/leads/:id/branchescrm:readupdatedPipelines a lead is in

Every pipeline the lead currently sits in, with its stage and owner per pipeline (branch_id null = General). A foreign or unknown lead id answers 404 (it answered 200 with an empty list).

Parameters

NameInTypeReq.Description
idpathuuidyesLead id

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/branches" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "branches": [
      {
        "branch_id": null,
        "branch_name": "General",
        "stage_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "stage_name": "New",
        "owner_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
      },
      {
        "branch_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "branch_name": "Enterprise",
        "stage_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "stage_name": "New",
        "owner_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundLead does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_lead_branches

POST/v1/crm/leads/:id/branchescrm:writenewAdd a lead to another pipeline

Places the lead in another pipeline as well (entry stage); it stays where it is. The branch and the optional owner are checked against this workspace.

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
branch_idbodystringyes"general" or a branch id
assignee_user_idbodyuuidOwner in that pipeline (default: current owner)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/branches" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "branch_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
  "assignee_user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
}'

Response

json
{
  "data": {
    "ok": true,
    "branches": [
      {
        "branch_id": null,
        "branch_name": "General",
        "stage_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "stage_name": "New",
        "owner_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
      },
      {
        "branch_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "branch_name": "Enterprise",
        "stage_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "stage_name": "New",
        "owner_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundLead or branch does not exist in this workspace
400invalid_requestassignee_user_id is not a workspace member

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_add_lead_to_branch

POST/v1/crm/leads/:id/branches/movecrm:writenewMove a lead between pipelines

Moves the lead out of one pipeline into another, to its entry stage or to to_stage_id (a stage of the destination). Reversible by moving back.

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
from_branch_idbodystringyes"general" or a branch id
to_branch_idbodystringyes"general" or a branch id
to_stage_idbodyuuidDestination stage
assignee_user_idbodyuuidOwner in the destination

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/branches/move" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from_branch_id": "general", "to_branch_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d"}'

Response

json
{
  "data": {
    "ok": true,
    "branches": [
      {
        "branch_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "branch_name": "Enterprise",
        "stage_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "stage_name": "New",
        "owner_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundLead or branch does not exist in this workspace
400invalid_requestSame pipeline, stage not in destination, or assignee not a member

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_move_lead_to_branch

POST/v1/crm/leads/:id/commentscrm:writeInternal team note on the lead card

Internal team note on the lead card (not visible to the lead). Mentioned users (ids from crm_list_workspace_members) get a CRM notification.

Parameters

NameInTypeReq.Description
idpathstringyes
bodybodystringyesmin length 1; max length 8000
mentionsbodystring[]User ids to @mention

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/comments" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body": "<body>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_add_comment

GET/v1/crm/leads/:id/conversationcrm:readFull message thread on the lead's source channel

The full message thread with this lead on their source channel (LinkedIn / email / widget / telegram / phone transcript). Read-only.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/conversation" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_conversation

POST/v1/crm/leads/:id/emailcrm:writenewconfirmSend an email from the lead card

Sends a REAL email to the lead (or to "to") from "<your name> via Vendo AI" with replies going to your own address, and logs it on the card. dry_run returns the exact draft; confirm sends. Workspace members need the crm-contact-details permission.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
subjectbodystringyes≤500 chars
bodybodystringyesPlain-text message
tobodystringRecipient; default the lead's email
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/email" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "subject": "Following up on our chat",
  "body": "Hi Amelia,\n\nAs promised, here is the deck…",
  "confirm": true
}'

Response

json
{
  "data": {
    "ok": true,
    "conversation": {
      "channel": "email",
      "messages": [
        {
          "direction": "outbound",
          "subject": "Following up on our chat",
          "sent_at": "2026-10-26T09:14:22.000Z"
        }
      ]
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundLead does not exist in this workspace
400invalid_requestMissing subject/body or no valid recipient
403forbiddenContact details are hidden for your role
502send_failedThe mail provider rejected the message

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_preview_email_to_lead, crm_send_email_to_lead

POST/v1/crm/leads/:id/reassigncrm:writeupdatedReassign a lead

Changes the lead owner (the new owner is notified). Body is now an explicit allow-list: assignee_user_id (public name; to_user_id also accepted) and optional reason. The MCP tool previously sent the forbidden owner_id field and always failed.

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
assignee_user_idbodyuuidyesNew owner from GET /v1/crm/workspace-members
reasonbodystring≤255

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/reassign" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "assignee_user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
  "reason": "Enterprise account"
}'

Response

json
{
  "data": {
    "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "assigned_user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
  }
}

Errors

HTTPCodeWhen
400invalid_requestMissing/invalid assignee or not a workspace member
404not_foundLead does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: reassign_crm_lead

POST/v1/crm/leads/:id/replycrm:writeSends a real message to the lead on their source channel (LinkedIn or email). Lead auto-moves to Manager Dialog stage

Send a LinkedIn message or email reply to the lead from the CRM card, real outbound communication under the customer's own accounts. Sends a REAL LinkedIn message or email reply to the lead on their source channel, from the customer's own account. Also moves the lead to the "Manager Dialog" stage. ALWAYS show the drafted text to the human and get approval BEFORE calling this.

Parameters

NameInTypeReq.Description
idpathstringyes
textbodystringyesmin length 1

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/reply" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "<text>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_reply_to_lead

PATCH/v1/crm/leads/:id/stagecrm:writeMove lead to another stage

Drag-and-drop equivalent: move a lead to a different pipeline stage by stage_id (get ids from crm_list_stages). Reversible.

Parameters

NameInTypeReq.Description
idpathstringyes
stage_idbodystringyes

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/stage" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stage_id": "<stage_id>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_move_lead

Pipelines (branches) & stages 11

GET/v1/crm/branchescrm:readList pipelines (branches)

Branches are isolated pipelines inside one workspace (teams / directions). Lists the pipelines you can see; see GET /v1/crm/branches/all for the full list with stages. branches = isolated pipelines inside one workspace (teams / directions). The assistant must know them before quoting numbers. Branches are isolated pipelines inside one workspace (teams / directions / clients). Returns id, name, is_default ("General"), member_user_ids. Read-only. Call before crm_stats / crm_list_leads when the user has more than one branch, then pass branch=<id> (or "general").

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/branches" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_branches

POST/v1/crm/branchescrm:writenewCreate a pipeline

Creates an isolated pipeline (team / direction / client) seeded with its own default stages. member_user_ids limits who sees it (General stays open to everyone). Workspace admins and owner only.

Parameters

NameInTypeReq.Description
namebodystringyes1..120 chars
member_user_idsbodyuuid[]User ids from GET /v1/crm/workspace-members

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/branches" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Enterprise", "member_user_ids": ["5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"]}'

Response

json
{
  "data": {
    "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "name": "Enterprise",
    "is_default": false,
    "member_user_ids": [
      "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b"
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestname missing
403forbiddenCaller is not a workspace admin/owner

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_create_branch

GET/v1/crm/branches/allcrm:readnewList all pipelines

Every pipeline (branch) of the workspace with id, name and is_default, wider than GET /v1/crm/branches for restricted members. Use it to pick a destination when adding or moving a lead.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/branches/all" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "branches": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "name": "General",
        "is_default": true
      },
      {
        "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "name": "Enterprise",
        "is_default": false
      }
    ]
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_all_branches

GET/v1/crm/branches/:idcrm:readnewGet one pipeline

One pipeline by id (id, name, is_default).

Parameters

NameInTypeReq.Description
idpathuuidyesBranch id

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/branches/$BRANCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "branch": {
      "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "name": "Enterprise",
      "is_default": false
    }
  }
}

Errors

HTTPCodeWhen
404not_foundBranch does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_branch

PATCH/v1/crm/branches/:idcrm:writenewRename a pipeline / change members

Renames a pipeline and/or replaces its member list. The General pipeline cannot be renamed or restricted. Admins and owner only.

Parameters

NameInTypeReq.Description
idpathuuidyesBranch id
namebodystringNew name
member_user_idsbodyuuid[]Replaces the member list

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/branches/$BRANCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Enterprise EMEA"}'

Response

json
{
  "data": {
    "ok": true,
    "branch": {
      "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "name": "Enterprise EMEA",
      "is_default": false
    }
  }
}

Errors

HTTPCodeWhen
404not_foundBranch does not exist in this workspace
400invalid_requestNothing to update, or General branch
403forbiddenCaller is not a workspace admin/owner

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_update_branch

DELETE/v1/crm/branches/:idcrm:writenewconfirmDelete a pipeline

Deletes a non-default pipeline with its stages and member list; its leads are not deleted, they fall back to General. dry_run shows the pipeline and its lead count. Admins and owner only.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesBranch id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/crm/branches/$BRANCH_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Delete the pipeline \"Enterprise\". Its own stages and member list are removed; its leads are NOT deleted — they fall back to the General pipeline. Only workspace admins/owner may do this. Cannot be undone.",
    "estimated": {
      "branch": {
        "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "name": "Enterprise",
        "is_default": false
      },
      "leads_in_branch": 37,
      "is_default": false
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundBranch does not exist in this workspace
400invalid_requestGeneral branch cannot be deleted
403forbiddenCaller is not a workspace admin/owner

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_crm_branch, delete_crm_branch

GET/v1/crm/pipeline-stagescrm:readupdatedList pipeline stages

Ordered stages of one pipeline. New: ?branch ("general" or a branch id), before, only the General pipeline's stages were reachable.

Parameters

NameInTypeReq.Description
branchquerystring"general" (default) or a branch id

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/pipeline-stages" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "stages": [
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "name": "New",
        "color": "#3b82f6",
        "order_index": 0,
        "is_terminal": false
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestbranch is empty, malformed or not in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_stages

POST/v1/crm/pipeline-stagescrm:writeCreate a custom stage (inserted before Won/Lost)

Add a custom kanban column to a pipeline (name, optional hex color, is_terminal for Won/Lost-like end stages). Inserted before the terminal stages. branch = "general" (default) or a branch id.

Parameters

NameInTypeReq.Description
namebodystringyesmin length 1
colorbodystring#RRGGBB
is_terminalbodyboolean
branchbodystring"general" (default) or a branch id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/pipeline-stages" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "<name>"}'

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_create_stage

POST/v1/crm/pipeline-stages/reordercrm:writeupdatedReorder pipeline stages

Sets the full left-to-right order of one pipeline's stages (the column drag in the UI). stage_ids must list every stage of that branch exactly once; the order applies to everyone in the workspace. Fixed (2026-10-26): an incomplete, foreign or duplicated list answers 400 with the missing / unknown ids (it answered 502); the order is left unchanged.

Parameters

NameInTypeReq.Description
stage_idsbodyuuid[]yesAll stage ids of the branch in the new order
branchbodystring"general" (default) or a branch id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/pipeline-stages/reorder" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "stage_ids": [
    "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
    "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90"
  ],
  "branch": "general"
}'

Response

json
{
  "data": {
    "stages": [
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "name": "New",
        "color": "#3b82f6",
        "order_index": 0,
        "is_terminal": false
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requeststage_ids is not exactly the full set of stages of that pipeline (the message lists missing, foreign and duplicated ids)

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_reorder_stages

PATCH/v1/crm/pipeline-stages/:idcrm:writeupdatedUpdate a pipeline stage

Renames / recolours a stage or changes is_terminal. Fixed (2026-10-26): a foreign or unknown stage id answers 404 not_found (it answered 400 with code internal_error).

Parameters

NameInTypeReq.Description
idpathuuidyesStage id from GET /v1/crm/pipeline-stages
namebodystring
colorbodystringHex colour
is_terminalbodyboolean

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/pipeline-stages/$STAGE_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Proposal sent", "color": "#046bd2"}'

Response

json
{
  "data": {
    "id": "5b8e1c3a-7d2f-4a9e-8c6b-0e4d2f1a9b57",
    "name": "Proposal sent",
    "color": "#046bd2",
    "order_index": 3,
    "is_terminal": false,
    "branch_id": null
  }
}

Errors

HTTPCodeWhen
404not_foundStage does not exist or is not in your workspace
400invalid_requestInvalid value

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_update_stage

DELETE/v1/crm/pipeline-stages/:idcrm:writeupdatedconfirmDelete a pipeline stage

Deletes a stage. Refused when it still holds leads or is a system stage (New / Manager Dialog). Destructive protocol. Fixed (2026-10-26): the dry run checks the stage and names it; a foreign or unknown stage id answers 404 for dry_run and confirm (it answered 200 / 400).

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesStage id
dry_runbodybooleantrue = preview only: nothing is changed (HTTP 200)
confirmbodybooleantrue = execute. Send exactly one of dry_run / confirm

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/crm/pipeline-stages/$STAGE_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Delete the pipeline stage \"Proposal sent\". Refused downstream if the stage still contains leads or is a system stage (New / Manager Dialog).",
    "stage": {
      "id": "5b8e1c3a-7d2f-4a9e-8c6b-0e4d2f1a9b57",
      "name": "Proposal sent",
      "branch_id": null,
      "is_terminal": false
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent (or both)
404not_foundStage does not exist or is not in your workspace
400invalid_requestStage still has leads or is a system stage

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_crm_stage, delete_crm_stage

Lead documents 4

GET/v1/crm/leads/:id/documentscrm:readnewList lead documents

Files attached to the lead card, newest first.

Parameters

NameInTypeReq.Description
idpathuuidyesLead id

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/documents" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "documents": [
      {
        "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
        "file_name": "proposal-ledgerly.pdf",
        "mime_type": "application/pdf",
        "size_bytes": 284113,
        "uploaded_by": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "uploaded_by_name": "Sam Carter",
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundLead does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_lead_documents

POST/v1/crm/leads/:id/documentscrm:writenewUpload lead documents

Attaches files to the lead card (private storage, activity entry). multipart field "files" (≤10 files × 25 MB) or JSON { files: [{ filename, content_base64 } | { file_url, filename? }] }. Allowed: pdf, doc(x), xls(x), csv, txt, rtf, odt, ods, ppt(x), png, jpg/jpeg, gif, webp, heic, zip.

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
filesbodyfile[] | arrayyesMultipart files, or JSON array of { filename, content_base64 } / { file_url }

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/documents" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"files": [{"filename": "proposal-ledgerly.pdf", "content_base64": "JVBERi0xLjcKJ…"}]}'

Response

json
{
  "data": {
    "documents": [
      {
        "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
        "file_name": "proposal-ledgerly.pdf",
        "mime_type": "application/pdf",
        "size_bytes": 284113,
        "uploaded_by": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "uploaded_by_name": "Sam Carter",
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
404not_foundLead does not exist in this workspace
400invalid_requestNo files or a file type is not accepted
413payload_too_largeFile or body too large

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_upload_lead_document

DELETE/v1/crm/leads/:id/documents/:docIdcrm:writenewconfirmDelete a lead document

Permanently deletes the file from the lead card and storage. dry_run shows which file.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
docIdpathuuidyesDocument id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/documents/$DOC_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'

Response

json
{
  "data": {
    "ok": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundDocument does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_delete_crm_lead_document, delete_crm_lead_document

GET/v1/crm/leads/:id/documents/:docId/filecrm:readnewDownload a lead document

Streams the file (PDF and images inline, everything else as attachment). ?format=base64 returns JSON { file_name, mime_type, size_bytes, content_base64 } for files up to 5 MB (used by MCP).

Parameters

NameInTypeReq.Description
idpathuuidyesLead id
docIdpathuuidyesDocument id
formatquerystring"base64" for a JSON envelope one of: base64

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/leads/$LEAD_ID/documents/$DOC_ID/file" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "file_name": "proposal-ledgerly.pdf",
    "mime_type": "application/pdf",
    "size_bytes": 284113,
    "content_base64": "JVBERi0xLjcKJ…"
  }
}

Errors

HTTPCodeWhen
404not_foundDocument does not exist in this workspace
413payload_too_largeformat=base64 on a file over 5 MB

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_lead_document

Communications 4

GET/v1/crm/communications/cardscrm:readupdatedCommunication cards

Read-only outbound stream (everyone contacted, replied or not). SECURITY FIX: now served through the same path as the app, so emails and phones are masked (null + contact_masked=true) for members without the "crm-contact-details" permission; it used to return them unmasked.

Parameters

NameInTypeReq.Description
limitqueryinteger1..500

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/communications/cards" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "cards": [
      {
        "type": "sdr",
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "name": "UK fintech funding",
        "sent": 640,
        "replied": 29,
        "last_activity_at": "2026-10-26T09:14:22.000Z"
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestBad type, id or limit

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_communication_cards

GET/v1/crm/communications/cards/:type/:id/historycrm:readupdatedOutbound history of a card

Read-only outbound stream (everyone contacted, replied or not). SECURITY FIX: now served through the same path as the app, so emails and phones are masked (null + contact_masked=true) for members without the "crm-contact-details" permission; it used to return them unmasked.

Parameters

NameInTypeReq.Description
typepathstringyesone of: campaign, sdr
idpathuuidyesCampaign or SDR search id
channelquerystringemail|linkedin|phone
repliedqueryboolean
fromquerydate
toquerydate
limitqueryinteger
offsetqueryinteger

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/communications/cards/$CARD_TYPE/$ID/history" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "rows": [
      {
        "contact_name": "Tom Reyes",
        "email": null,
        "contact_masked": true,
        "channel": "email",
        "replied": true,
        "sent_at": "2026-10-26T09:14:22.000Z",
        "key": "tom@brightpay.io"
      }
    ],
    "total": 1
  }
}

Errors

HTTPCodeWhen
400invalid_requestBad type, id or limit

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_communication_history

GET/v1/crm/communications/cards/:type/:id/threadcrm:readupdatedMessage thread of one contact

Read-only outbound stream (everyone contacted, replied or not). SECURITY FIX: now served through the same path as the app, so emails and phones are masked (null + contact_masked=true) for members without the "crm-contact-details" permission; it used to return them unmasked. The card id must be one of your campaigns (type campaign) or SDR searches (type sdr); otherwise 404 (the id used to be ignored).

Parameters

NameInTypeReq.Description
typepathstringyesone of: campaign, sdr
idpathuuidyes
keyquerystringyesThread key from a history row

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/communications/cards/$CARD_TYPE/$ID/thread?key=$KEY" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "rows": [
      {
        "contact_name": "Tom Reyes",
        "email": null,
        "contact_masked": true,
        "channel": "email",
        "replied": true,
        "sent_at": "2026-10-26T09:14:22.000Z",
        "key": "tom@brightpay.io"
      }
    ],
    "total": 1
  }
}

Errors

HTTPCodeWhen
400invalid_requestBad type, id or limit
404not_foundThe card (campaign or SDR search) does not exist or belongs to another workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_communication_thread

POST/v1/crm/communications/promotecrm:writenewPromote a contact to a lead

Turns a contacted person from the Communications stream into a CRM lead in the New stage (manual source). Deduplicated per workspace by email / LinkedIn / phone: returns the existing lead with already_existed=true.

Parameters

NameInTypeReq.Description
emailbodystringAt least one of email / linkedin_url / phone
linkedin_urlbodystringhttp(s) URL
phonebodystring
contact_namebodystringFull name
company_namebodystring
titlebodystring
avatar_urlbodystring
assigned_user_idbodyuuidOwner (default: you)

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/communications/promote" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "tom@brightpay.io", "contact_name": "Tom Reyes", "company_name": "BrightPay"}'

Response

json
{
  "data": {
    "lead_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
    "already_existed": false
  }
}

Errors

HTTPCodeWhen
400invalid_requestNo email/linkedin_url/phone, or assignee not a member

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_promote_contact_to_lead

Calendar 4

GET/v1/crm/calendar/eventscrm:readCustom calendar events. Optional from / to (ISO dates)

Custom events only. SDR sending windows also appear in the UI calendar, but they are derived from the search schedule and are not editable, read them via GET /v1/sdr/searches/:id instead. List custom calendar events (meetings, reminders) in the CRM planner for a date range. Note: SDR sending windows also appear in the UI calendar but are derived from the search schedule, read those via sdr_get_search. Read-only, free.

Parameters

NameInTypeReq.Description
fromquerystringISO date, start of range
toquerystringISO date, end of range

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/calendar/events" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_calendar_events

POST/v1/crm/calendar/eventscrm:writeupdatedCreate a calendar event

Creates a custom CRM calendar event (call, meeting, task…) optionally linked to a lead. Answers 201. Fixed (2026-10-26): a lead_id that is not a lead of your workspace answers 404 (it was silently dropped and the event created unlinked).

Parameters

NameInTypeReq.Description
titlebodystringyesEvent title
start_atbodydatetimeyesISO 8601
end_atbodydatetimeISO 8601
lead_idbodyuuidLead to link (GET /v1/crm/leads)
descriptionbodystring

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/calendar/events" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Call Sarah (Northgate Dental)",
  "start_at": "2026-10-28T10:00:00Z",
  "end_at": "2026-10-28T10:30:00Z",
  "lead_id": "2c9e7a4b-1d3f-4b8e-9a6c-5f0d2e1b8a37"
}'

Response

json
{
  "data": {
    "id": "9a4e2c7b-3d1f-4e8a-b6c5-0f2d9e1a7b48",
    "title": "Call Sarah (Northgate Dental)",
    "start_at": "2026-10-28T10:00:00.000Z",
    "end_at": "2026-10-28T10:30:00.000Z",
    "lead_id": "2c9e7a4b-1d3f-4b8e-9a6c-5f0d2e1b8a37",
    "created_at": "2026-10-26T14:10:00.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestMissing title/start_at or lead_id is not a UUID
404not_foundlead_id is not a lead of your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_create_calendar_event

PATCH/v1/crm/calendar/events/:idcrm:writeupdatedUpdate a calendar event

Updates a custom calendar event. Fixed (2026-10-26): a foreign or unknown event id answers 404 (it answered 400 "Event not found"); a lead_id that is not a lead of your workspace answers 404 (it used to unlink the event's real lead silently).

Parameters

NameInTypeReq.Description
idpathuuidyesEvent id
titlebodystring
start_atbodydatetime
end_atbodydatetime
lead_idbodyuuidLead to link; null unlinks

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/calendar/events/$EVENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"start_at": "2026-10-28T11:00:00Z"}'

Response

json
{
  "data": {
    "id": "9a4e2c7b-3d1f-4e8a-b6c5-0f2d9e1a7b48",
    "title": "Call Sarah (Northgate Dental)",
    "start_at": "2026-10-28T11:00:00.000Z",
    "lead_id": "2c9e7a4b-1d3f-4b8e-9a6c-5f0d2e1b8a37"
  }
}

Errors

HTTPCodeWhen
404not_foundEvent or lead_id does not exist or is not in your workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_update_calendar_event

DELETE/v1/crm/calendar/events/:idcrm:writeDelete a calendar event. No confirmation step required

Not gated by dry_run/confirm: a calendar event is a low-value, easily recreated record, unlike a lead or a pipeline stage, deleting one destroys no history and cannot cascade. Remove a custom calendar event. Unlike deleting a lead or a stage this destroys no history and needs no preview step, but it cannot be undone.

Parameters

NameInTypeReq.Description
idpathstringyes

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/crm/calendar/events/$EVENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_delete_calendar_event

CRM notifications 3

GET/v1/crm/notificationscrm:readupdatedList CRM notifications

Latest 50 CRM notifications of the caller (assigned, reassigned, mentioned, new_lead, stage_change) with total and unread counts. unread_only=true now really filters (it was ignored downstream); the unused limit parameter was removed.

Parameters

NameInTypeReq.Description
unread_onlyquerybooleanOnly unread

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/notifications" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 58,
    "unread": 4,
    "rows": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "type": "new_lead",
        "lead_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "payload": {
          "lead_name": "Tom Reyes",
          "source": "email_reply_real"
        },
        "read_at": null,
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ]
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_notifications

POST/v1/crm/notifications/read-allcrm:writeMark all notifications as read

Clear the CRM notification badge by marking everything read.

Parameters

No parameters.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/crm/notifications/read-all" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_mark_all_notifications_read

PATCH/v1/crm/notifications/:id/readcrm:writeupdatedMark a CRM notification read

Marks one CRM notification as read. Fixed (2026-10-26): the id must be one of your notifications (among the latest 50, or the latest 50 unread), a foreign or unknown id answers 404 (it answered 200 {ok:true}); an already-read notification is left unchanged (idempotent) and answers already_read: true.

Parameters

NameInTypeReq.Description
idpathuuidyesNotification id from GET /v1/crm/notifications

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/notifications/$NOTIFICATION_ID/read" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "ok": true
  }
}

Errors

HTTPCodeWhen
404not_foundNotification does not exist or is not yours

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_mark_notification_read

Settings & lookups 6

GET/v1/crm/agentscrm:readAI agents in this workspace that leads can be attributed to

List the AI agents in this workspace that CRM leads can be attributed to. Read-only, free.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/agents" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_workspace_agents

GET/v1/crm/linkedin-accountscrm:readLinkedIn accounts connected to this workspace, as shown in the CRM

List the LinkedIn accounts connected to this workspace, as shown in the CRM. Useful to see which account owns a conversation. Read-only, free.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/linkedin-accounts" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_linkedin_accounts

GET/v1/crm/settingscrm:readRead CRM settings, including the notify_* toggles

These control whether Vendo emails/notifies on new leads, replies and stage changes. Exposed so an integration can mute notifications while it drives the CRM itself. Read CRM settings, including the notify_* toggles that control which events send an email/notification. Read-only, free.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_get_settings

PATCH/v1/crm/settingscrm:writeupdatedUpdate CRM settings

Workspace CRM settings with an explicit, type-checked allow-list of the real fields: notify_new_lead, notify_reassigned, notify_mention, notify_email, notify_in_app, stale_alert_days (1..90), default_date_range, excluded_agent_ids, excluded_sources, excluded_linkedin_account_ids. Unknown fields are rejected instead of being merged into the settings row.

Parameters

NameInTypeReq.Description
notify_new_leadbodyboolean
notify_reassignedbodyboolean
notify_mentionbodyboolean
notify_emailbodyboolean
notify_in_appbodyboolean
stale_alert_daysbodyinteger1..90
default_date_rangebodystringone of: 24h, 7d, 30d, 90d, 180d, 1y, all
excluded_agent_idsbodyuuid[]
excluded_sourcesbodystring[]
excluded_linkedin_account_idsbodyuuid[]

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/crm/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"notify_email": false, "stale_alert_days": 7}'

Response

json
{
  "data": {
    "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "excluded_agent_ids": [],
    "excluded_sources": [],
    "excluded_linkedin_account_ids": [],
    "default_date_range": "30d",
    "notify_new_lead": true,
    "notify_reassigned": true,
    "notify_mention": true,
    "notify_email": false,
    "notify_in_app": true,
    "stale_alert_days": 7
  }
}

Errors

HTTPCodeWhen
400invalid_requestWrong type, out of range or nothing to update

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_update_settings

GET/v1/crm/statscrm:readPer-stage counts, new today/this week, stale leads

Counts per stage, new today/this week, stale leads. Read-only. Optional branch ("general" or a branch id from crm_list_branches), omitted = all branches visible to the user. If the workspace has more than one branch, call crm_list_branches first and report per branch or ask which one.

Parameters

NameInTypeReq.Description
branchquerystring

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/stats" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_stats

GET/v1/crm/workspace-memberscrm:readPeople who can own a lead. Call this before reassign to get valid owner ids

POST /leads/:id/reassign shipped without any way to discover a valid assignee, so callers were reassigning blind. These three make it usable. List the people in this workspace who can own a CRM lead. Use before reassign_crm_lead to get valid owner ids. Read-only, free.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/crm/workspace-members" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: crm_list_workspace_members

Workspace & account API 30

Team and invitations, your profile and notification preferences, in-app notifications, CRM integrations and the referral program.

Team 6

GET/v1/workspaceworkspace:readnewGet workspace and team

Workspace name and owner, members with role and feature permissions, pending invitations, and whether the caller is admin / owner. feature_ids lists the values accepted by the permissions route.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/workspace" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (62 lines)
json
{
  "data": {
    "workspace": {
      "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "name": "Sam's Workspace",
      "owner_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "created_at": "2026-10-26T09:14:22.000Z",
      "updated_at": "2026-10-26T09:14:22.000Z"
    },
    "members": [
      {
        "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "user_id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "email": "sam@ledgerly.co.uk",
        "first_name": "Sam",
        "last_name": "Carter",
        "avatar_url": null,
        "role": "admin",
        "permissions": null,
        "joined_at": "2026-10-26T09:14:22.000Z"
      },
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
        "email": "priya@ledgerly.co.uk",
        "first_name": "Priya",
        "last_name": "Shah",
        "avatar_url": null,
        "role": "member",
        "permissions": [
          "crm",
          "analytics"
        ],
        "joined_at": "2026-10-26T09:14:22.000Z"
      }
    ],
    "pendingInvitations": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "email": "li@ledgerly.co.uk",
        "status": "pending",
        "created_at": "2026-10-26T09:14:22.000Z",
        "expires_at": "2026-11-02T09:14:22.000Z"
      }
    ],
    "isAdmin": true,
    "amOwner": true,
    "myPermissions": null,
    "feature_ids": [
      "dashboard",
      "my-agents",
      "sales-process",
      "vendo-sdr",
      "audiences",
      "crm",
      "analytics",
      "billing",
      "settings",
      "crm-contact-details"
    ]
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_workspace

POST/v1/workspace/invitationsworkspace:writenewconfirmInvite a teammate

Sends a real invitation email (link valid 7 days); on accepting, the person joins with full access. dry_run checks whether the invite is possible without sending. Admins only.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
emailbodystringyesInvitee email
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/workspace/invitations" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "li@ledgerly.co.uk", "confirm": true}'

Response

json
{
  "data": {
    "success": true,
    "message": "Invitation sent to li@ledgerly.co.uk",
    "invitation": {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "email": "li@ledgerly.co.uk",
      "expires_at": "2026-11-02T09:14:22.000Z"
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
400invalid_requestInvalid email, already a member, or invitation pending
403forbiddenOnly workspace admins can invite

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_invite_workspace_member, invite_workspace_member

DELETE/v1/workspace/invitations/:idworkspace:writenewCancel an invitation

Cancels a pending invitation; its link stops working. Admins only.

Parameters

NameInTypeReq.Description
idpathuuidyesInvitation id

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/workspace/invitations/$INVITATION_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true,
    "message": "Invitation cancelled"
  }
}

Errors

HTTPCodeWhen
404not_foundInvitation does not exist in this workspace
403forbiddenOnly workspace admins can cancel invitations

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: cancel_workspace_invitation

DELETE/v1/workspace/members/:userIdworkspace:writenewconfirmRemove a teammate

Removes a member from the workspace: they immediately lose access to its agents, campaigns, CRM and credits; their account keeps its own new empty workspace. Admins only; dry_run shows who would be removed.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
userIdpathuuidyesMember user_id
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/workspace/members/$USER_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

json
{
  "data": {
    "dry_run": true,
    "would_do": "Remove priya@ledgerly.co.uk from \"Sam's Workspace\". They immediately lose access to its agents, campaigns, CRM and credits (their account stays and gets its own empty workspace). Re-adding needs a new invitation.",
    "estimated": {
      "member": {
        "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
        "email": "priya@ledgerly.co.uk",
        "role": "member"
      },
      "caller_is_admin": true
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundMember does not exist in this workspace
400invalid_requestYou cannot remove yourself
403forbiddenOnly workspace admins can remove members

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_remove_workspace_member, remove_workspace_member

PATCH/v1/workspace/members/:userId/permissionsworkspace:writenewSet a member's feature access

Sets the allow-list of features a regular member can use (null = full access). "crm-contact-details" decides whether they see lead emails and phones. Admins only; the owner and admins cannot be restricted.

Parameters

NameInTypeReq.Description
userIdpathuuidyesMember user_id
permissionsbodystring[] | nullyesFeature ids or null one of: dashboard, my-agents, sales-process, vendo-sdr, audiences, crm, analytics, billing, settings, crm-contact-details

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/workspace/members/$USER_ID/permissions" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"permissions": ["crm", "analytics"]}'

Response

json
{
  "data": {
    "success": true,
    "message": "Member access updated",
    "permissions": [
      "crm",
      "analytics"
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestUnknown feature id
403forbiddenCaller is not admin, or target is owner/admin
404not_foundMember does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_workspace_member_permissions

PATCH/v1/workspace/members/:userId/roleworkspace:writenewconfirmPromote / demote an admin

admin=true makes the member an admin (full access, manages team and pipelines); false makes them a regular member. Owner only; dry_run shows the change.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
userIdpathuuidyesMember user_id
adminbodybooleanyestrue = admin, false = member
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/workspace/members/$USER_ID/role" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"admin": true, "confirm": true}'

Response

json
{
  "data": {
    "success": true,
    "message": "Member promoted to admin",
    "admin": true
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundMember does not exist in this workspace
403forbiddenOnly the workspace owner can change roles

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_set_workspace_member_role, set_workspace_member_role

Profile & preferences 5

GET/v1/meworkspace:readnewGet my profile

Profile of the API key owner (no secrets, no 2FA state).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/me" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "email": "sam@ledgerly.co.uk",
    "first_name": "Sam",
    "last_name": "Carter",
    "company_name": "Ledgerly",
    "position": "Head of Growth",
    "avatar_url": "/uploads/avatars/7d2f….png",
    "auth_provider": "local",
    "email_verified": true,
    "google_calendar_connected": false,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_profile

PATCH/v1/meworkspace:writenewUpdate my profile

Updates first_name (1..100), last_name (≤100), company_name (≤200) and position (≤200). Email, password, 2FA and account deletion stay in the app.

Parameters

NameInTypeReq.Description
first_namebodystring
last_namebodystring
company_namebodystring
positionbodystring

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/me" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"position": "VP Growth"}'

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "email": "sam@ledgerly.co.uk",
    "first_name": "Sam",
    "last_name": "Carter",
    "company_name": "Ledgerly",
    "position": "VP Growth",
    "avatar_url": "/uploads/avatars/7d2f….png",
    "auth_provider": "local",
    "email_verified": true,
    "google_calendar_connected": false,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestEmpty first_name, too long, or nothing to update

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_profile

PATCH/v1/me/avatarworkspace:writenewSet my profile photo

Uploads a JPG/PNG/GIF (≤5 MB) as the profile photo: multipart field "avatar", or JSON { image_url } (fetched server-side) or { content_base64, filename }.

Parameters

NameInTypeReq.Description
avatarbodyfile (multipart)Image file
image_urlbodystringPublic image URL
content_base64bodystringBase64 image
filenamebodystringWith content_base64, e.g. me.png

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/me/avatar" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://example.com/photos/sam.png"}'

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "email": "sam@ledgerly.co.uk",
    "first_name": "Sam",
    "last_name": "Carter",
    "company_name": "Ledgerly",
    "position": "Head of Growth",
    "avatar_url": "/uploads/avatars/91ab….png",
    "auth_provider": "local",
    "email_verified": true,
    "google_calendar_connected": false,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestNot a JPG/PNG/GIF or larger than 5 MB
400forbidden_urlimage_url resolves to a private address

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: set_profile_avatar

GET/v1/me/notification-preferencesworkspace:readnewGet my notification preferences

Personal notification switches (Settings › Notifications).

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/me/notification-preferences" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "email_notifications": true,
    "in_app_notifications": true,
    "meeting_reminders": true,
    "credit_alerts": true,
    "analytics_emails_enabled": false
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_notification_preferences

PATCH/v1/me/notification-preferencesworkspace:writenewUpdate my notification preferences

Turns personal notifications on or off: email_notifications, in_app_notifications, meeting_reminders, credit_alerts, analytics_emails_enabled. Turning them off stops alerts to the human.

Parameters

NameInTypeReq.Description
email_notificationsbodyboolean
in_app_notificationsbodyboolean
meeting_remindersbodyboolean
credit_alertsbodyboolean
analytics_emails_enabledbodyboolean

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/me/notification-preferences" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"analytics_emails_enabled": true}'

Response

json
{
  "data": {
    "email_notifications": true,
    "in_app_notifications": true,
    "meeting_reminders": true,
    "credit_alerts": true,
    "analytics_emails_enabled": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestNon-boolean value or nothing to update

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_notification_preferences

In-app notifications 5

GET/v1/notificationsworkspace:readnewList in-app notifications

The caller's bell notifications (meetings, billing, low credits, invitations…), newest first, with unread count and paging. CRM lead notifications are separate (GET /v1/crm/notifications).

Parameters

NameInTypeReq.Description
pagequeryintegerDefault 1
limitqueryinteger1..100, default 20

Request

curl
curl -s "https://api.vendo-ai.com/v1/notifications" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "notifications": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "type": "LOW_CREDITS",
        "subject": "Your credit balance is running low",
        "preview": "You have 48 credits left…",
        "isRead": false,
        "createdAt": "2026-10-26T09:14:22.000Z",
        "readAt": null
      }
    ],
    "total": 34,
    "unreadCount": 3,
    "page": 1,
    "limit": 20,
    "totalPages": 2
  }
}

Errors

HTTPCodeWhen
400invalid_requestpage/limit out of range

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_app_notifications

POST/v1/notifications/read-allworkspace:writenewMark all notifications read

Marks every unread in-app notification of the caller as read.

Parameters

No parameters.

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/notifications/read-all" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true,
    "markedCount": 3
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: mark_all_app_notifications_read

GET/v1/notifications/unread-countworkspace:readnewUnread notifications count

The bell badge number.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/notifications/unread-count" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "unreadCount": 3
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_app_notifications_unread_count

DELETE/v1/notifications/:idworkspace:writenewDelete a notification

Removes one notification from the caller's list (soft delete).

Parameters

NameInTypeReq.Description
idpathuuidyesNotification id

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/notifications/$NOTIFICATION_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
404not_foundNotification does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: delete_app_notification

POST/v1/notifications/:id/readworkspace:writenewMark a notification read

Marks one in-app notification as read.

Parameters

NameInTypeReq.Description
idpathuuidyesNotification id

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/notifications/$NOTIFICATION_ID/read" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "success": true
  }
}

Errors

HTTPCodeWhen
404not_foundNotification does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: mark_app_notification_read

CRM integrations 9

GET/v1/integrationsworkspace:readnewList CRM integrations

External CRM connections of the workspace (Kommo, HubSpot) with status, account, settings and counters. Tokens and secrets are never returned.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/integrations" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "provider": "KOMMO",
      "status": "ACTIVE",
      "account_name": "Ledgerly Sales",
      "account_url": "https://ledgerly.kommo.com",
      "connected_at": "2026-10-26T09:14:22.000Z",
      "last_sync_at": "2026-10-26T09:14:22.000Z",
      "has_custom_credentials": true,
      "stats": {
        "total_synced_conversations": 214,
        "total_synced_contacts": 198,
        "total_created_leads": 87
      },
      "settings": {
        "sync_conversations": true,
        "sync_contacts": true,
        "create_leads": true,
        "auto_sync": true
      }
    }
  ]
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_crm_integrations

POST/v1/integrations/kommo/credentialsworkspace:writenewSave Kommo app credentials

Kommo private integration step 1: stores the Integration ID (a UUID) and Secret key of the Vendo app the client registered in their Kommo account. The secret is redacted in logs and never returned. Next: POST /v1/integrations/kommo/connect.

Parameters

NameInTypeReq.Description
client_idbodyuuidyesKommo Integration ID
client_secretbodystringyesKommo Secret key

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/integrations/kommo/credentials" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"client_id": "9c31e135-f366-4178-846d-d56c8b7bfa2c", "client_secret": "••••••••"}'

Response

json
{
  "data": {
    "saved": true,
    "redirect_uri": "https://api.vendo-ai.com/integrations/kommo/callback",
    "next": "POST /v1/integrations/kommo/connect"
  }
}

Errors

HTTPCodeWhen
400invalid_requestMissing values or client_id is not a UUID

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: save_kommo_credentials

GET/v1/integrations/kommo/pipelinesworkspace:readnewList Kommo pipelines

Pipelines of the connected Kommo account with their statuses (stages), the values for pipeline_config.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/integrations/kommo/pipelines" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": 7712345,
      "name": "Sales",
      "is_main": true,
      "_embedded": {
        "statuses": [
          {
            "id": 61234567,
            "name": "Incoming leads",
            "sort": 10
          }
        ]
      }
    }
  ]
}

Errors

HTTPCodeWhen
404not_foundKommo is not connected / not active

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_kommo_pipelines

GET/v1/integrations/kommo/usersworkspace:readnewList Kommo users

Users of the connected Kommo account, values for pipeline_config.responsible_user_id.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/integrations/kommo/users" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": 10987654,
      "name": "Sam Carter",
      "email": "sam@ledgerly.co.uk"
    }
  ]
}

Errors

HTTPCodeWhen
404not_foundKommo is not connected / not active

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_kommo_users

GET/v1/integrations/:providerworkspace:readnewGet one CRM integration

Status and settings of the Kommo or HubSpot connection; { connected: false, provider } when none exists. Poll it after starting OAuth.

Parameters

NameInTypeReq.Description
providerpathstringyeskommo or hubspot one of: kommo, hubspot

Request

curl
curl -s "https://api.vendo-ai.com/v1/integrations/$PROVIDER" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "provider": "KOMMO",
    "status": "ACTIVE",
    "connected": true,
    "account_name": "Ledgerly Sales",
    "account_url": "https://ledgerly.kommo.com",
    "has_custom_credentials": true,
    "sync_conversations": true,
    "sync_contacts": true,
    "create_leads": true,
    "auto_sync": true,
    "pipeline_config": {
      "pipeline_id": "7712345",
      "stage_id": "61234567",
      "responsible_user_id": "10987654"
    },
    "field_mapping": {},
    "last_sync_at": "2026-10-26T09:14:22.000Z",
    "total_synced_conversations": 214,
    "total_synced_contacts": 198,
    "total_created_leads": 87,
    "last_error": null,
    "last_error_at": null,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestUnknown provider

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_crm_integration

DELETE/v1/integrations/:providerworkspace:writenewconfirmDisconnect a CRM integration

Removes the connection: syncing stops and the stored tokens, credentials and settings are deleted; reconnecting needs OAuth again. Only the person who connected it can disconnect it.

Protected: send {"dry_run": true} to preview, then {"confirm": true} to execute (protocol).

Parameters

NameInTypeReq.Description
providerpathstringyeskommo or hubspot one of: kommo, hubspot
dry_runbodybooleantrue = preview only, nothing changes
confirmbodybooleantrue = execute. Exactly one of dry_run / confirm is required

Request

curl
curl -s -X DELETE "https://api.vendo-ai.com/v1/integrations/$PROVIDER" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

Response

Show the full response (35 lines)
json
{
  "data": {
    "dry_run": true,
    "would_do": "Disconnect Kommo (Ledgerly Sales): syncing stops and the stored tokens, credentials and settings are deleted. Reconnecting needs the OAuth flow again. Only the person who connected it can disconnect it.",
    "estimated": {
      "integration": {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "provider": "KOMMO",
        "status": "ACTIVE",
        "connected": true,
        "account_name": "Ledgerly Sales",
        "account_url": "https://ledgerly.kommo.com",
        "has_custom_credentials": true,
        "sync_conversations": true,
        "sync_contacts": true,
        "create_leads": true,
        "auto_sync": true,
        "pipeline_config": {
          "pipeline_id": "7712345",
          "stage_id": "61234567",
          "responsible_user_id": "10987654"
        },
        "field_mapping": {},
        "last_sync_at": "2026-10-26T09:14:22.000Z",
        "total_synced_conversations": 214,
        "total_synced_contacts": 198,
        "total_created_leads": 87,
        "last_error": null,
        "last_error_at": null,
        "created_at": "2026-10-26T09:14:22.000Z",
        "updated_at": "2026-10-26T09:14:22.000Z"
      }
    }
  }
}

Errors

HTTPCodeWhen
400confirmation_requiredNeither dry_run nor confirm was sent
400bad_requestBoth dry_run and confirm were sent
404not_foundThe caller has no connection for this provider

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: preview_disconnect_crm_integration, disconnect_crm_integration

POST/v1/integrations/:provider/connectworkspace:writenewStart OAuth for Kommo / HubSpot

Returns the provider authorization URL that a human must open in a browser and approve within 20 minutes; the provider callback completes the connection server-side. Kommo requires saved credentials first.

Parameters

NameInTypeReq.Description
providerpathstringyeskommo or hubspot one of: kommo, hubspot

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/integrations/$PROVIDER/connect" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "url": "https://www.kommo.com/oauth?client_id=9c31e135-…&state=4f1c…&mode=post_message",
    "state": "4f1c9a…",
    "expires_in_seconds": 1200,
    "next": "Open the url in a browser logged in to Kommo and approve, then poll GET /v1/integrations/kommo until status is ACTIVE."
  }
}

Errors

HTTPCodeWhen
400invalid_requestUnknown provider or Kommo credentials not configured

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: connect_crm_integration

GET/v1/integrations/:provider/logsworkspace:readnewCRM integration sync log

Recent sync events of the caller's own connection (contact/lead created, errors), newest first.

Parameters

NameInTypeReq.Description
providerpathstringyeskommo or hubspot one of: kommo, hubspot
limitqueryinteger1..200, default 50

Request

curl
curl -s "https://api.vendo-ai.com/v1/integrations/$PROVIDER/logs" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": [
    {
      "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
      "event_type": "LEAD_CREATED",
      "status": "SUCCESS",
      "conversation_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
      "crm_contact_id": "22918311",
      "crm_lead_id": "18822001",
      "error_message": null,
      "created_at": "2026-10-26T09:14:22.000Z"
    }
  ]
}

Errors

HTTPCodeWhen
404not_foundThe caller has no connection for this provider

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_crm_integration_logs

PATCH/v1/integrations/:provider/settingsworkspace:writenewUpdate CRM integration settings

What is synced and where: sync_conversations, sync_contacts, create_leads, auto_sync, pipeline_config (Kommo pipeline / stage / responsible user) and field_mapping. Changes what Vendo writes into the external CRM.

Parameters

NameInTypeReq.Description
providerpathstringyeskommo or hubspot one of: kommo, hubspot
sync_conversationsbodyboolean
sync_contactsbodyboolean
create_leadsbodyboolean
auto_syncbodyboolean
pipeline_configbodyobject{ pipeline_id, stage_id, responsible_user_id }
field_mappingbodyobjectstring → string map

Request

curl
curl -s -X PATCH "https://api.vendo-ai.com/v1/integrations/$PROVIDER/settings" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "pipeline_config": {
    "pipeline_id": "7712345",
    "stage_id": "61234567",
    "responsible_user_id": "10987654"
  },
  "create_leads": true
}'

Response

json
{
  "data": {
    "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "provider": "KOMMO",
    "status": "ACTIVE",
    "connected": true,
    "account_name": "Ledgerly Sales",
    "account_url": "https://ledgerly.kommo.com",
    "has_custom_credentials": true,
    "sync_conversations": true,
    "sync_contacts": true,
    "create_leads": true,
    "auto_sync": true,
    "pipeline_config": {
      "pipeline_id": "7712345",
      "stage_id": "61234567",
      "responsible_user_id": "10987654"
    },
    "field_mapping": {},
    "last_sync_at": "2026-10-26T09:14:22.000Z",
    "total_synced_conversations": 214,
    "total_synced_contacts": 198,
    "total_created_leads": 87,
    "last_error": null,
    "last_error_at": null,
    "created_at": "2026-10-26T09:14:22.000Z",
    "updated_at": "2026-10-26T09:14:22.000Z"
  }
}

Errors

HTTPCodeWhen
400invalid_requestWrong types or nothing to update
404not_foundNo connection for this provider

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: update_crm_integration_settings

Referral program 5

GET/v1/referralworkspace:readnewReferral overview

Program terms (commission %, minimum payout, link base), totals (visits, signups, paying customers, revenue, earned, paid out, pending, available), every link with stats, and payout request statuses (amounts and status only). Payout requests can only be made in the app.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/referral" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (36 lines)
json
{
  "data": {
    "program": {
      "commission_pct": 20,
      "min_payout_usd": 50,
      "link_base": "https://vendo-ai.com/?ref="
    },
    "totals": {
      "visits": 412,
      "signups": 9,
      "paying_customers": 2,
      "revenue_usd": 1180,
      "earned_usd": 236,
      "paid_out_usd": 0,
      "pending_usd": 0,
      "available_usd": 236
    },
    "links": [
      {
        "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "code": "K7Q2XH",
        "label": "LinkedIn post",
        "url": "https://vendo-ai.com/?ref=K7Q2XH",
        "created_at": "2026-10-26T09:14:22.000Z",
        "archived_at": null,
        "visits": 412,
        "signups": 9,
        "paying_customers": 2,
        "revenue_usd": 1180,
        "earned_usd": 236
      }
    ],
    "payouts": [],
    "referred_users_count": 9
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_referral_overview

POST/v1/referral/linksworkspace:writenewCreate a referral link

Creates a tracked referral link (up to 20 active links).

Parameters

NameInTypeReq.Description
labelbodystring≤80 chars, where you will share it

Request

curl
curl -s -X POST "https://api.vendo-ai.com/v1/referral/links" \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "Newsletter"}'

Response

json
{
  "data": {
    "id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "code": "P3M9TA",
    "label": "Newsletter",
    "url": "https://vendo-ai.com/?ref=P3M9TA",
    "created_at": "2026-10-26T09:14:22.000Z",
    "archived_at": null,
    "visits": 0,
    "signups": 0,
    "paying_customers": 0,
    "revenue_usd": 0,
    "earned_usd": 0
  }
}

Errors

HTTPCodeWhen
400invalid_requestLabel too long or 20 active links already

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: create_referral_link

GET/v1/referral/referred-usersworkspace:readnewReferred users

People who signed up through the caller's links: masked email, link, signup and first-payment dates, revenue, commission and free/paying status.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/referral/referred-users" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "referred_users": [
      {
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "link_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
        "link_code": "K7Q2XH",
        "email_masked": "j***@brightpay.io",
        "signed_up_at": "2026-10-26T09:14:22.000Z",
        "first_payment_at": null,
        "revenue_usd": 0,
        "earned_usd": 0,
        "status": "free"
      }
    ],
    "total": 9
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: list_referred_users

Analytics & planner API 4

Cross-product analytics and the planner calendar.

Analytics 3

GET/v1/analytics/agentsanalytics:readnewAgents analytics page

Every metric of the /analytics/agents page for a UTC date range, optionally one agent: totals, rates, daily series, funnel (widget loads → opens → conversations → contacts → leads), by agent and channel, leads by source, recent leads, credits by type, languages, pages, traffic sources, hours, durations and the previous period.

Parameters

NameInTypeReq.Description
fromquerydateYYYY-MM-DD
toquerydateYYYY-MM-DD
agent_idqueryuuidNarrow to one agent

Request

curl
curl -s "https://api.vendo-ai.com/v1/analytics/agents" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (62 lines)
json
{
  "data": {
    "range": {
      "from": "2026-10-01",
      "to": "2026-10-26",
      "days": [
        "2026-10-01",
        "…"
      ]
    },
    "previous": {
      "totals": {
        "conversations": 141,
        "leads": 22
      }
    },
    "agents": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "name": "Ava — Ledgerly",
        "agent_type": "widget",
        "status": "active"
      }
    ],
    "totals": {
      "conversations": 168,
      "contacts": 61,
      "leads": 27,
      "voice_calls": 12,
      "chats": 156,
      "credits": 912
    },
    "rates": {
      "contact_rate": 0.36,
      "lead_rate": 0.16
    },
    "daily": {
      "conversations": [
        4,
        7,
        6
      ]
    },
    "funnel": {
      "widget_loads": 18240,
      "widget_opens": 1310,
      "conversations": 168,
      "contacts": 61,
      "leads": 27
    },
    "by_agent": [],
    "by_channel": [],
    "leads_by_source": [],
    "recent_leads": [],
    "credits_by_type": [],
    "languages": [],
    "pages": [],
    "traffic_sources": [],
    "hours": [],
    "durations": []
  }
}

Errors

HTTPCodeWhen
400invalid_requestBad date format or from > to
404not_foundAgent does not exist in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_analytics_agents

GET/v1/analytics/campaignsanalytics:readnewSales Process analytics

Workspace KPIs for classic campaigns (Analytics page): totals, open/click/reply rates and per-campaign performance, optionally for a date range. For SDR + campaigns combined use GET /v1/analytics/sdr.

Parameters

NameInTypeReq.Description
fromquerydateYYYY-MM-DD
toquerydateYYYY-MM-DD

Request

curl
curl -s "https://api.vendo-ai.com/v1/analytics/campaigns" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "summary": {
      "totalCampaigns": 6,
      "activeCampaigns": 2,
      "totalEmailsSent": 4820,
      "totalOpened": 1902,
      "totalClicked": 133,
      "totalReplies": 121,
      "totalContacts": 2210,
      "openRate": 39.5,
      "clickRate": 2.8,
      "replyRate": 2.5
    },
    "campaignPerformance": [
      {
        "id": "c3a7e2f4-1b5d-4e8a-9c0f-6d2b8a4e7f15",
        "name": "UK SaaS founders — Q4",
        "status": "active",
        "emailsSent": 1260,
        "opened": 512,
        "replied": 37,
        "openRate": 40.6,
        "replyRate": 2.9
      }
    ]
  }
}

Errors

HTTPCodeWhen
400invalid_requestA parameter has the wrong type, range or enum value.

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_campaign_analytics

GET/v1/analytics/sdranalytics:readnewSDR & outreach analytics page

Everything on the /analytics/sdr page for a UTC date range: Vendo SDR and classic campaigns together, or one SDR search (search_id) or one campaign (campaign_id), never both. Includes delivery and inbox placement, funnel, by product / source / type, enrichment and per-process rows. (The SDR domain also exposes a similar GET /v1/sdr/analytics.)

Parameters

NameInTypeReq.Description
fromquerydateYYYY-MM-DD
toquerydateYYYY-MM-DD
search_idqueryuuidOne SDR search
campaign_idqueryuuidOne campaign

Request

curl
curl -s "https://api.vendo-ai.com/v1/analytics/sdr" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (56 lines)
json
{
  "data": {
    "workspace_id": "8a1d4c3b-2e5f-4a6b-9d7c-1e2f3a4b5c6d",
    "search_id": null,
    "campaign_id": null,
    "range": {
      "from": "2026-10-01",
      "to": "2026-10-26",
      "days": [
        "2026-10-01",
        "…"
      ]
    },
    "totals": {
      "signals": 1840,
      "contacts": 622,
      "sent": 1904,
      "replies": 71
    },
    "rates": {
      "reply_rate": 0.037
    },
    "delivery": {
      "sent": 1904,
      "delivered": 1861,
      "bounced": 43,
      "inbox_placement": {
        "inbox": 0.91,
        "spam": 0.09
      }
    },
    "by_product": {
      "sdr": {
        "sent": 1210
      },
      "classic": {
        "sent": 694
      }
    },
    "by_source": [],
    "by_type": [],
    "contacts_by_source": [],
    "enrichment": {},
    "notifications_by_type": [],
    "processes": [
      {
        "kind": "sdr",
        "id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "name": "UK fintech funding",
        "sent": 640,
        "replies": 29
      }
    ],
    "funnel": {}
  }
}

Errors

HTTPCodeWhen
400invalid_requestBoth ids given, bad date format or from > to
404not_foundsearch_id / campaign_id not in this workspace

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_analytics_sdr

Planner 1

GET/v1/planner/eventscampaigns:readnewPlanner sending windows

The planner layer of the calendar page: per day and channel sending windows of every campaign and SDR search (paused flag, planned today, sent today) with campaign / search info. Wide ranges are materialised only up to a bounded window, range says what was returned.

Parameters

NameInTypeReq.Description
fromquerydatetimeyesISO date/time
toquerydatetimeyesISO date/time

Request

curl
curl -s "https://api.vendo-ai.com/v1/planner/events?from=$FROM&to=$TO" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (36 lines)
json
{
  "data": {
    "events": [
      {
        "id": "campaign:3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90:2026-10-27",
        "type": "campaign_window",
        "start": "2026-10-27T09:00:00.000Z",
        "end": "2026-10-27T17:00:00.000Z",
        "timezone": "Europe/London",
        "all_day": false,
        "channels": [
          {
            "channel": "email",
            "paused": false,
            "planned_today": 120,
            "sent_today": 0
          }
        ],
        "campaign": {
          "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
          "name": "Q4 CFO outreach",
          "status": "active",
          "agent_id": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
          "daily_limit": 120,
          "color": "#2563eb",
          "kind": "classic"
        }
      }
    ],
    "range": {
      "from": "2026-10-27T00:00:00.000Z",
      "to": "2026-11-03T00:00:00.000Z",
      "truncated": false
    }
  }
}

Errors

HTTPCodeWhen
400invalid_requestfrom/to missing, not ISO, or from > to

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_planner_events

Billing (read-only) API 14

Balance, subscription, plan limits, pricing, transactions, invoices and credit usage. Read-only by design.

Billing 14

GET/v1/billing/balancebilling:readupdatedCredit balance

Read-only. FIX: credits are shared per workspace and owned by the workspace owner, the gateway now resolves the owner first (fail-closed), so member keys see the workspace numbers and a read can never create a free-trial balance row for the member.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/balance" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "balance": 2140
  }
}

Errors

HTTPCodeWhen
502internal_errorWorkspace owner could not be resolved

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_balance

GET/v1/billing/balance/fullbilling:readBalance + plan summary + spent totals

balance + plan label + workspace owner. Maps to vendo-billing's GET /api/billing/credits/balance which already resolves workspace owner and returns the richer body. Richer than get_balance: returns balance, total subscription credits, total purchased, total spent, free-trial credits, email/workspace info.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/balance/full" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_balance_full

GET/v1/billing/currenciesbilling:readnewSupported currencies

Currencies Vendo bills in (code, symbol, name) plus the account's own billing currency from the current subscription. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/currencies" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "total": 3,
    "currencies": [
      {
        "code": "USD",
        "symbol": "$",
        "name": "US Dollar"
      },
      {
        "code": "GBP",
        "symbol": "£",
        "name": "British Pound"
      },
      {
        "code": "EUR",
        "symbol": "€",
        "name": "Euro"
      }
    ],
    "account_currency": "GBP"
  }
}

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_supported_currencies

GET/v1/billing/invoicesbilling:readStripe invoices (paid + open)

Stripe invoices (paid + open). Optional ?year=YYYY&month=MM to scope. List of Stripe invoices (paid + open). Optional year/month scope.

Parameters

NameInTypeReq.Description
yearqueryinteger
monthqueryintegermin 1; max 12

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/invoices" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_invoices

GET/v1/billing/pricingbilling:readupdatedPricing catalogue

Public price list: subscription plans (type SUBSCRIPTION, name e.g. "Starter"), credit top-ups and per-operation costs (operation_type, cost_in_credits), with currency, price, included credits and max_agents (null = unlimited). Optional ?currency=USD|GBP|EUR returns one currency. Fixed (2026-10-26): rows no longer expose the internal stripe_price_id_encrypted; the plan name is the name field (there is no plan field).

Parameters

NameInTypeReq.Description
currencyquerystringone of: USD, GBP, EUR

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/pricing" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (40 lines)
json
{
  "data": [
    {
      "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
      "name": "Starter",
      "description": "For one sales agent",
      "type": "SUBSCRIPTION",
      "currency": "GBP",
      "price": 49,
      "credits": 1000,
      "operation_type": null,
      "cost_in_credits": null,
      "max_agents": 1,
      "is_active": true,
      "is_public": true,
      "metadata": {
        "tier": 1
      },
      "created_at": "2026-06-05T09:00:00.000Z",
      "updated_at": "2026-10-01T09:00:00.000Z"
    },
    {
      "id": "8b1e4c7a-2d5f-4a9b-9c3e-6f0a1d2b3c4e",
      "name": "Text generation",
      "description": null,
      "type": "OPERATION_COST",
      "currency": "GBP",
      "price": 0,
      "credits": 0,
      "operation_type": "TEXT_GENERATION",
      "cost_in_credits": 1,
      "max_agents": null,
      "is_active": true,
      "is_public": true,
      "metadata": null,
      "created_at": "2026-06-05T09:00:00.000Z",
      "updated_at": "2026-06-05T09:00:00.000Z"
    }
  ]
}

Errors

HTTPCodeWhen
400invalid_requestUnsupported currency

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_pricing

GET/v1/billing/sdr/email-capacitybilling:readSDR sender capacity (included + extras, daily limit)

Vendo SDR managed sender entitlement (included addresses, purchased extras, estimated daily emails). How many SDR sender email addresses the account has (included + purchased extras) and the estimated daily email throughput. Read-only.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/sdr/email-capacity" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_sdr_email_capacity

GET/v1/billing/subscriptionbilling:readCurrent subscription (plan, status, dates)

current plan, status, period_end, cancel_at. Plan, status (ACTIVE/CANCELLED/PAST_DUE/EXPIRED/TRIALING), period dates, currency, amount, Stripe IDs.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/subscription" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_subscription

GET/v1/billing/subscription/limitsbilling:readupdatedPlan limits

Read-only. FIX: credits are shared per workspace and owned by the workspace owner, the gateway now resolves the owner first (fail-closed), so member keys see the workspace numbers and a read can never create a free-trial balance row for the member.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/subscription/limits" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "maxAgents": 5,
    "baseMaxAgents": 3,
    "extraAgentSlots": 2,
    "plan": "SCALE",
    "status": "ACTIVE",
    "isLegacy": false,
    "currency": "GBP"
  }
}

Errors

HTTPCodeWhen
502internal_errorWorkspace owner could not be resolved

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_subscription_limits

GET/v1/billing/transactionsbilling:readupdatedCredit transactions

Workspace credit transactions, newest first. type / from / to filter (vendo-billing pages by limit/offset only, so the gateway scans newest → oldest until it passes from, at most 5000 rows). Fixed (2026-10-26): the scan stopped after the first page of matches, so filtered reads always returned total_matched: null and a hard-coded note. Now total_matched is exact whenever the scan reaches the start of the range (or the oldest row); only when 5000 rows are not enough is it null, with a note giving the real number of rows scanned. Without filters it is a plain page.

Parameters

NameInTypeReq.Description
typequerystringTransaction type, e.g. EMAIL_OUTREACH, SDR_CONTACT, PURCHASE, SUBSCRIPTION_GRANT
fromquerydateISO
toquerydateISO; date-only is inclusive
limitqueryinteger1..200, default 50
offsetqueryinteger

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/transactions" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "transactions": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "user_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
        "type": "SDR_CONTACT",
        "amount": -2,
        "balance_after": 2140,
        "description": "SDR contact for search UK fintech funding",
        "agent_id": null,
        "created_at": "2026-10-26T09:14:22.000Z"
      }
    ],
    "total_matched": 1,
    "has_more": false,
    "limit": 50,
    "offset": 0,
    "filters": {
      "type": "SDR_CONTACT",
      "from": "2026-10-01",
      "to": "2026-10-26"
    },
    "scanned": 412
  }
}

Errors

HTTPCodeWhen
400invalid_requestfrom/to not ISO or from > to

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_transactions

GET/v1/billing/usage/agents/:agentIdbilling:readSpend for one agent

credits spent by one agent. Total credits spent by one specific agent.

Parameters

NameInTypeReq.Description
agentIdpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/usage/agents/$AGENT_ID" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_usage_for_agent

GET/v1/billing/usage/agents/:agentId/connectionsbilling:readPer-channel spend for one agent

spend by connection. Per-channel credit spend (widget, telegram, phone, etc.) for one agent. Identifies the costliest channels.

Parameters

NameInTypeReq.Description
agentIdpathstringyes

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/usage/agents/$AGENT_ID/connections" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Returns { "data": … } with the resource(s) described above.

Errors

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_usage_for_agent_connections

GET/v1/billing/usage/by-agentbilling:readupdatedSpend per agent

Read-only. FIX: credits are shared per workspace and owned by the workspace owner, the gateway now resolves the owner first (fail-closed), so member keys see the workspace numbers and a read can never create a free-trial balance row for the member.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/usage/by-agent" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "userId": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
    "agents": [
      {
        "agentId": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "creditsSpent": 912
      }
    ]
  }
}

Errors

HTTPCodeWhen
502internal_errorWorkspace owner could not be resolved

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_usage_by_agent

GET/v1/billing/usage/by-typebilling:readupdatedSpend sessions by type

Per-session credit spend (newest first) with connectionType and agent. from/to filter (scan of the newest rows, up to 5000) and add totals_by_type for the range. Fixed (2026-10-26): totals_by_type is always present and total_matched is exact when the scan reaches from; totals_complete says whether the totals cover the whole range (false only when 5000 rows were not enough, the note then gives the real number scanned).

Parameters

NameInTypeReq.Description
fromquerydateISO
toquerydateISO; date-only is inclusive
limitqueryinteger1..500, default 50
offsetqueryinteger

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/usage/by-type" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

Show the full response (33 lines)
json
{
  "data": {
    "sessions": [
      {
        "id": "3f9c2a1e-7b4d-4e2a-9c1f-2d8e5a6b7c90",
        "date": "2026-10-26T09:14:22.000Z",
        "agentId": "c2b7e9a4-5d1f-4c8e-a3b6-9f0e1d2c3b4a",
        "agentName": "Ava — Ledgerly",
        "agentType": "widget",
        "connectionType": "widget",
        "creditsSpent": 1,
        "description": "Text message",
        "metadata": null
      }
    ],
    "total_matched": 1,
    "has_more": false,
    "limit": 50,
    "offset": 0,
    "totals_by_type": {
      "widget": {
        "credits": 1,
        "count": 1
      }
    },
    "filters": {
      "from": "2026-10-26",
      "to": "2026-10-26"
    },
    "scanned": 37,
    "totals_complete": true
  }
}

Errors

HTTPCodeWhen
400invalid_requestfrom/to not ISO or from > to

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_usage_by_type

GET/v1/billing/usage/monthlybilling:readupdatedMonthly spend breakdown

Read-only. FIX: credits are shared per workspace and owned by the workspace owner, the gateway now resolves the owner first (fail-closed), so member keys see the workspace numbers and a read can never create a free-trial balance row for the member.

Parameters

No parameters.

Request

curl
curl -s "https://api.vendo-ai.com/v1/billing/usage/monthly" \
  -H "Authorization: Bearer $VENDO_API_KEY"

Response

json
{
  "data": {
    "month": "2026-10",
    "totalSpent": 1860,
    "breakdown": [
      {
        "type": "EMAIL_OUTREACH",
        "amount": 1120,
        "count": 1120
      }
    ]
  }
}

Errors

HTTPCodeWhen
502internal_errorWorkspace owner could not be resolved

Plus the common errors (401 missing_api_key/bad_api_key, 403 missing_scope, 404 not_found for ids outside your workspace, 429 rate_limited).

MCP: get_credit_usage_monthly

MCP server

The Model Context Protocol lets AI assistants call external tools. The Vendo AI MCP server exposes the whole /v1 API as 396 tools, so Claude, ChatGPT, Cursor, VS Code, Windsurf or your own agent can create agents, run Vendo SDR, launch campaigns and work the CRM in plain English.

  • Endpoint: https://api.vendo-ai.com/mcp: Streamable HTTP (POST JSON-RPC, GET for the server event stream, DELETE to close; the server returns an mcp-session-id header to send on later requests).
  • Auth: the same API key, as Authorization: Bearer vnd_live_… (or X-API-Key) on the MCP connection. There is no OAuth flow; clients that cannot send a header can use the mcp-remote bridge shown below.
  • Same rules as REST: each tool calls the documented REST route with your key, same scopes, rate limits, credits, audit log and workspace isolation. The MCP server holds no secrets of its own.
  • Without a key the server allows initialize and tools/list (for registries and discovery); tool calls then fail with 401.
  • Errors are tool results: a failed call returns isError: true with {"error": true, "code", "message", "status"} over HTTP 200. Rate limiting reads rate_limited, retry after N s (plus retry_after_seconds), retry the same tool after N seconds.

MCP setup

Claude Code

bash
claude mcp add --transport http vendo-ai https://api.vendo-ai.com/mcp \
  --header "Authorization: Bearer vnd_live_YOUR_KEY"

# check it
claude mcp list

Claude Desktop

Add to claude_desktop_config.json (Settings › Developer › Edit Config) and restart. The mcp-remote bridge (needs Node.js) forwards the Authorization header to the remote server.

json, claude_desktop_config.json
{
  "mcpServers": {
    "vendo-ai": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.vendo-ai.com/mcp",
        "--header",
        "Authorization:${VENDO_AUTH}"
      ],
      "env": {
        "VENDO_AUTH": "Bearer vnd_live_YOUR_KEY"
      }
    }
  }
}

Cursor

json, ~/.cursor/mcp.json
{
  "mcpServers": {
    "vendo-ai": {
      "url": "https://api.vendo-ai.com/mcp",
      "headers": {
        "Authorization": "Bearer vnd_live_YOUR_KEY"
      }
    }
  }
}

VS Code (GitHub Copilot agent mode)

json, .vscode/mcp.json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "vendo-key",
      "description": "Vendo AI API key",
      "password": true
    }
  ],
  "servers": {
    "vendo-ai": {
      "type": "http",
      "url": "https://api.vendo-ai.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:vendo-key}"
      }
    }
  }
}

Windsurf

json, ~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "vendo-ai": {
      "serverUrl": "https://api.vendo-ai.com/mcp",
      "headers": {
        "Authorization": "Bearer vnd_live_YOUR_KEY"
      }
    }
  }
}

ChatGPT and the OpenAI API

ChatGPT connectors that let you set a custom Authorization header can use the URL and header above. With the OpenAI Responses API, pass the header on the MCP tool:

python, OpenAI Responses API
# OpenAI Responses API — remote MCP tool
from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
    model="gpt-5",
    tools=[{
        "type": "mcp",
        "server_label": "vendo-ai",
        "server_url": "https://api.vendo-ai.com/mcp",
        "headers": {"Authorization": "Bearer vnd_live_YOUR_KEY"},
        "require_approval": "always",
    }],
    input="List my Vendo AI agents and their status.",
)
print(resp.output_text)

Claude API (MCP connector)

bash, Anthropic Messages API
# Claude Messages API — MCP connector (beta)
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: mcp-client-2025-11-20" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 4096,
    "mcp_servers": [{
      "type": "url",
      "url": "https://api.vendo-ai.com/mcp",
      "name": "vendo-ai",
      "authorization_token": "vnd_live_YOUR_KEY"
    }],
    "tools": [{"type": "mcp_toolset", "mcp_server_name": "vendo-ai"}],
    "messages": [{"role": "user", "content": "Show my Vendo AI credit balance and active campaigns."}]
  }'

Any MCP client / raw HTTP

bash
# Raw Streamable HTTP: initialize, then list tools
curl -s -D - https://api.vendo-ai.com/mcp \
  -H "Authorization: Bearer $VENDO_API_KEY" \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# copy the mcp-session-id response header, then:
curl -s https://api.vendo-ai.com/mcp \
  -H "Authorization: Bearer $VENDO_API_KEY" -H "mcp-session-id: $SESSION" \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Safety model: preview / execute tool pairs

MCP has no dry_run flag. Instead, each protected action is split into two tools: the preview tool runs dry_run and returns what would happen; the execute tool runs confirm. Tool descriptions instruct the model to call the execute tool only after the user has seen the preview, and because the tool name carries the intent, a model cannot launch or delete by accident. Destructive tools that act on a single call (such as sending a reply) say so in their description. Configure your client to ask for approval on execute tools if you want a human in the loop for every call.

All 50 preview → execute pairs
Preview toolExecute tool
preview_deep_scandeep_scan_agent
preview_delete_agent_connectiondelete_agent_connection
preview_delete_narrator_leaddelete_narrator_lead
preview_disconnect_google_meetdisconnect_google_meet
preview_cancel_meetingcancel_meeting
preview_remove_agent_from_meetingremove_agent_from_meeting
preview_send_telegram_messagesend_telegram_message
preview_setup_phone_agentsetup_phone_agent
preview_phone_test_callphone_test_call
preview_connect_whatsapp_accountconnect_whatsapp_account
preview_disconnect_telegram_botdisconnect_telegram_bot
preview_disconnect_whatsapp_accountdisconnect_whatsapp_account
preview_archive_campaignarchive_campaign
preview_launch_campaignlaunch_campaign
preview_schedule_campaign_deliveryschedule_campaign_delivery
preview_send_campaign_emails_nowsend_campaign_emails_now
preview_reply_to_campaign_emailreply_to_campaign_email
preview_reply_in_campaign_threadreply_in_campaign_thread
preview_update_campaign_discoveryupdate_campaign_discovery
preview_disconnect_client_mailboxdisconnect_client_mailbox
preview_activate_campaign_linkedinactivate_campaign_linkedin
preview_send_linkedin_messagesend_linkedin_message
preview_activate_campaign_phoneactivate_campaign_phone
preview_disconnect_content_accountdisconnect_content_account
preview_delete_custom_audience_contactdelete_custom_audience_contact
preview_delete_custom_audiencedelete_custom_audience
preview_enrich_custom_audienceenrich_custom_audience
preview_delete_crm_branchdelete_crm_branch
preview_delete_crm_lead_documentdelete_crm_lead_document
preview_disconnect_crm_integrationdisconnect_crm_integration
preview_invite_workspace_memberinvite_workspace_member
preview_set_workspace_member_roleset_workspace_member_role
preview_remove_workspace_memberremove_workspace_member
sdr_preview_start_researchsdr_start_research
sdr_preview_delete_searchsdr_delete_search
sdr_preview_run_nowsdr_run_now
sdr_preview_set_person_groupsdr_set_person_group
sdr_preview_delete_personsdr_delete_person
sdr_preview_bulk_delete_peoplesdr_bulk_delete_people
sdr_preview_launch_outreachsdr_launch_outreach
preview_delete_agentdelete_agent
sdr_preview_delete_outreachsdr_delete_outreach
preview_delete_campaigndelete_campaign
preview_delete_crm_leaddelete_crm_lead
preview_delete_crm_stagedelete_crm_stage
preview_buy_audience_setbuy_audience_set
preview_launch_audience_setlaunch_audience_set
preview_reconnect_linkedin_accountreconnect_linkedin_account
preview_disconnect_linkedin_accountdisconnect_linkedin_account
crm_preview_email_to_leadcrm_send_email_to_lead

MCP tool reference 396

All 396 tools of the Vendo AI MCP server, grouped like the REST reference. Each card lists the tool's arguments (generated from its JSON Schema, Req. marks required arguments; nested fields use dotted names) and the REST route(s) it calls, so every tool behaves exactly like the documented endpoint, with the same scopes, limits and errors.

Agents tools 100

Agent core & lifecycle 14

create_agentCreate a new agentupdated

Create an AI agent like step 1 of the app's builder. Choose its direction: purpose "sales" (default), "support" or "info". The agent starts in DRAFT and contacts nobody. Needs at least 1 credit on the balance (checked, not charged). Fails with a plan-limit error when all agent slots are used (see get_agent_plan_limits). Next: set_agent_company → generate_agent_script → channels.

ArgumentTypeReq.Description
namestringyesAgent's display name, e.g. 'Anna from Acme'
descriptionstring1-2 sentences about what the agent does
purposestringsales = sales agent, support = customer support, info = information assistant one of: sales, support, info
genderstringVoice/face gender (default female) one of: male, female
avatar_presetstringPreset face avatar-1 … avatar-20 (male: 1,4,6,8,10-15; female: 2,3,5,7,9,16-20). For your own photo use set_agent_avatar_from_url afterwards pattern ^avatar-([1-9]|1[0-9]|20)$

Calls: POST /v1/agents

delete_agentDelete an agent (irreversible)

Permanently delete an agent. NOT reversible. Only call after the user has reviewed preview_delete_agent output and explicitly approved.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/agents/:id

duplicate_agentDuplicate an agent

Create a copy of an existing agent (new id, same config). Counts toward agent capacity.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/agents/:id/duplicate

get_agentGet an agent

Fetch the full details of a single agent by id.

ArgumentTypeReq.Description
idstringyesThe agent UUID

Calls: GET /v1/agents/:id

get_agent_plan_limitsGet agent capacity

How many agents can the user create (base plan + purchased extra slots) vs how many they have. Read-only.

No arguments.

Calls: GET /v1/agents/plan-limits

list_agentsList agents

List all AI agents in the account. Read-only.

No arguments.

Calls: GET /v1/agents

preview_delete_agentPreview deleting an agentpreview

Show what would happen if you delete an agent. The agent is NOT deleted. ALWAYS call this first and surface the result to the user before calling delete_agent.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/agents/:id

set_agent_avatar_from_urlSet the agent's own photoupdated

Upload a custom avatar photo (JPG, PNG or GIF, ≤5 MB) from a public image URL or base64. Replaces the preset face. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
image_urlstringPublic https URL of the image
image_base64stringBase64 or data: URL of the image (use instead of image_url)

Calls: PATCH /v1/agents/:id/avatar

set_agent_companySet company info (name / website / description)

Required before generate_agent_script will work. Set the company the agent represents, its name, website, and what it sells.

ArgumentTypeReq.Description
idstringyes
company_namestringyes
company_websitestringyes
company_descriptionstringyes

Calls: PATCH /v1/agents/:id/company

set_agent_contact_channelsSet the contacts the agent hands outupdated

Set the meeting link, presentation link, email, phone, WhatsApp, Telegram and up to 10 custom links that the agent gives to visitors (also shown as screen cards). MERGE: only the channels you pass change; pass "" (or null for custom_links) to clear one. Write, free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
meeting_urlstringBooking link, e.g. Calendly (http/https)
presentation_urlstringLink to a presentation/deck (http/https)
emailstring
phonestring
whatsappstring
telegramstring
custom_linksobject[] | nullReplaces the whole custom link list; null or [] clears it max items 10
custom_links[].labelstringyesmax length 60
custom_links[].urlstringyes
custom_links[].notestringWhen the agent should share it max length 200

Calls: PATCH /v1/agents/:id/contact-channels

set_agent_integrationsSet legacy integration flagsnew

Legacy flags on the agent (zoom_enabled, google_meet_enabled, website_embed_enabled). Channels are managed with the connection tools; this does not connect anything. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
zoom_enabledboolean
google_meet_enabledboolean
website_embed_enabledboolean

Calls: PATCH /v1/agents/:id/integrations

set_agent_statusPause / resume / archive an agent

Change agent status. Allowed values: ACTIVE, PAUSED, ARCHIVED, DRAFT.

ArgumentTypeReq.Description
idstringyes
statusstringyesone of: ACTIVE, PAUSED, ARCHIVED, DRAFT

Calls: PATCH /v1/agents/:id/status

update_agentUpdate agent basicsupdated

Change an agent's name, description, direction (purpose), voice gender or preset face. Only the fields you pass change. Company name/website/description → set_agent_company; contacts → set_agent_contact_channels. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
namestring
descriptionstring
purposestringone of: sales, support, info
genderstringone of: male, female
avatar_presetstringPreset face avatar-1 … avatar-20 pattern ^avatar-([1-9]|1[0-9]|20)$

Calls: PATCH /v1/agents/:id

update_agent_scriptEdit the agent's script stagesnew

Replace the script stages (rename, reorder, edit, add or remove stages) and optionally the tone, like the builder's script editor. Send the FULL list of stages you want; the full text is rebuilt from them. Get the current stages from get_agent (script_sections). Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
sectionsobject[]yesAll stages in order min items 1
sections[].idstring
sections[].titlestringyes
sections[].contentstringyes
sections[].iconstring
script_stylestringTone; omit to keep the current one one of: PROFESSIONAL_FORMAL, FRIENDLY_CASUAL, TECHNICAL_EXPERT, CONSULTATIVE
email_scriptstringWritten-channel script (email/LinkedIn); omit to keep

Calls: PATCH /v1/agents/:id/script

Builder: website scan, description, script 10

deep_scan_agentDeep-scan the company website (15 credits)updatedexecute

Read the company website + LinkedIn and extract description, team, employee count, contacts, social links and pricing. Costs 15 credits, takes 60-180 s. With persist=true (default) the result is SAVED like in the app: company_description replaced, team/contacts/social/pricing stored (creates company + people records, fills blank contact channels). Waits up to ~200 s; if still running it returns a job_id, then call get_agent_job. Only call after preview_deep_scan and the user's approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
company_namestringDefaults to the agent's company_name
company_websitestringDefaults to the agent's company_website
persistbooleanSave the result onto the agent (default true)
auto_updateobjectRe-scan automatically
auto_update.enabledbooleanyes
auto_update.intervalstringyesone of: daily, weekly, biweekly, monthly, quarterly

Calls: POST /v1/agents/:id/deep-scan

generate_agent_descriptionDraft the company description from the websiteupdated

AI-write "what the agent knows" about the company from its website (no full scan). Costs 1 credit. NOT saved, pass the text to set_agent_company (company_description).

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
company_namestringDefaults to the agent's company_name
company_websitestringDefaults to the agent's company_website
target_languagestringWrite in this language, e.g. "Ukrainian"

Calls: POST /v1/agents/:id/generate-description

generate_agent_page_descriptionsDescribe website pages with AInew

Write AI descriptions + keywords for website pages (output of scan_agent_website). Costs 1 credit per 5 pages. Not saved.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
pagesobject[]yesmin items 1
pages[].pathstringyes
pages[].titlestringyes

Calls: POST /v1/agents/:id/generate-page-descriptions

generate_agent_scriptGenerate the agent's scriptupdated

Generate the agent's script (stages + full text + email/LinkedIn script) in the chosen tone for its direction (sales / support / info), using the company description and uploaded files. Saves it on the agent (overwrites). Costs 1 credit, takes 60-90 s. Needs company_name and company_description (set_agent_company). If it is still running after ~200 s you get a job_id → get_agent_job.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
script_stylestringyesTone one of: PROFESSIONAL_FORMAL, FRIENDLY_CASUAL, TECHNICAL_EXPERT, CONSULTATIVE
purposestringDefaults to the agent's direction one of: sales, support, info

Calls: POST /v1/agents/:id/generate-script

generate_support_scriptGenerate a support knowledge script (legacy)new

Legacy support-script generator of the old text-widget creators. Prefer generate_agent_script with purpose "support". Needs company name + description. Costs 1 credit.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: POST /v1/agents/:id/generate-support-script

preview_deep_scanPreview a deep website scanupdatedpreview

Show what deep_scan_agent would do: cost (15 credits), duration (60-180 s) and what gets saved. Nothing runs. ALWAYS call first and get the user's approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
company_namestring
company_websitestring
persistboolean

Calls: POST /v1/agents/:id/deep-scan

save_scan_dataSave scan facts onto the agentupdated

Save company facts (team, contacts, social links, pricing, employee count, auto re-scan) onto the agent, what the app does after a scan. Side effects: stores them in the agent, creates/updates the company and people records, fills BLANK contact channels (never overwrites manual ones). Not needed after deep_scan_agent with persist=true. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
company_namestringyes
company_websitestringyes
directorsobject[]Team / management
directors[].namestringyes
directors[].positionstringyes
directors[].emailstring
directors[].phonestring
directors[].linkedinstring
contactsobject
contacts.addressesstring[]
contacts.phonesstring[]
contacts.emailsstring[]
social_linksobject
social_links.linkedinstring
social_links.facebookstring
social_links.instagramstring
social_links.twitterstring
social_links.whatsappstring
social_links.telegramstring
social_links.calendlystring
social_links.bookingstring
pricingstring
employee_countnumber | string
auto_updateobject
auto_update.enabledbooleanyes
auto_update.intervalstringyesone of: daily, weekly, biweekly, monthly, quarterly

Calls: POST /v1/agents/:id/save-scan-data

scan_agent_websiteScan the agent's website structurenew

List the pages of a website (path, title, description, keywords) for this agent, used by the page-navigation feature. Costs 2 credits. Read-only for the agent (nothing saved).

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
website_urlstringyes

Calls: POST /v1/agents/:id/scan-website

scan_website_quickQuick website scan

Crawl a website's structure (sitemap, top pages) BEFORE creating an agent. Useful when deciding which company to set on the agent. Costs ~2 credits.

ArgumentTypeReq.Description
website_urlstringyes

Calls: POST /v1/agents/scan

test_agent_replyAsk the agent a test question (text)new

Send a message to the agent and get its reply using its current script, a text test, nobody is contacted. Needs a generated script. Use history for a multi-turn test.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
messagestringyesWhat the visitor says
historyobject[]Earlier turns, oldest first
history[].rolestringyesone of: user, assistant
history[].contentstringyes

Calls: POST /v1/agents/:id/generate-response

Long-running jobs 2

get_agent_jobGet a long job (deep scan / script) resultnew

Status and result of a deep scan or script generation job started by deep_scan_agent / generate_agent_script (kept 1 hour). Poll every 10-20 s while status is "running". Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
job_idstringyes

Calls: GET /v1/agents/:id/jobs/:jobId

list_agent_jobsList recent long jobs of an agentnew

Deep scans and script generations of this agent in the last hour (status only), use when a job_id was lost. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: GET /v1/agents/:id/jobs

Knowledge 3

add_agent_documentAttach a knowledge file to the agentnew

Upload a material the agent learns from (PDF, DOC, DOCX, TXT, XLS, XLSX, CSV, PPT, PPTX, KEY; ≤10 MB) from a public URL or base64. Used the next time the script is generated (generate_agent_script). Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
filenamestringyesFile name with extension, e.g. "price-list.pdf"
file_urlstringPublic https URL of the file
file_base64stringBase64 content (use instead of file_url)

Calls: POST /v1/agents/:id/documents

generate_knowledge_from_urlTurn a web page into a knowledge blocknew

Read a public web page and return a knowledge text block about it. Costs 1 credit. NOT saved, append the result to company_description with set_agent_company if you want the agent to know it.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
urlstringyesPublic http(s) page URL

Calls: POST /v1/agents/:id/generate-block-from-url

remove_agent_documentRemove a knowledge file from the agentnew

Remove an uploaded material from the agent (see get_agent → documents[].url). Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
urlstringdocuments[].url (preferred)
filenamestringdocuments[].filename if you have no url

Calls: DELETE /v1/agents/:id/documents

Voice 3

list_retell_voicesList available Retell voices

Catalogue of voices that can be assigned via set_agent_voice. Read-only.

No arguments.

Calls: GET /v1/agents/voices/retell

list_voice_catalogList voices (Voice v2 catalogue)new

All voices for the website widget, Slide Presenter and Telegram voice: engine, gender, accent, expressive support, Ukrainian support, recommended flag, preview audio. Read-only. Use a voice_id from here in set_agent_voice / set_presentation_voice / set_telegram_voice_settings.

No arguments.

Calls: GET /v1/agents/voices/catalog

set_agent_voiceTurn on / configure the website-widget voiceupdated

Configure the voice of the agent's website widget exactly like the app's widget settings page and create/update the voice agent at the voice provider, so voice calls in the widget really work. Only the settings you pass change (the rest keep their saved values or the app defaults). voice_id is required the first time (list_voice_catalog). Takes up to ~1 min. Free to configure; widget voice calls cost 14 credits/min.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
voice_idstringVoice id from list_voice_catalog (e.g. "retell-Grace", "11labs-Adrian")
speednumberSpeaking pace, 0.7-1.3 (1 = normal) min 0.7; max 1.3
emotionstringDelivery: neutral, expressive (warm), calm, dynamic (energetic) one of: neutral, expressive, calm, dynamic
languagesstring[]Retell locales like ["en-US","uk-UA"]; more than one = multilingual (answers in the caller's language). [] or one locale = script language only max items 20
rulesstringCommunication rules (plain text instructions for how the agent talks). Omit to keep the saved ones max length 8000
ambientstringBackground sound; "" = none one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumenumbermin 0; max 2
backchannelbooleanShort "mm-hm" acknowledgements while the person talks
backchannel_frequencynumbermin 0; max 1
end_call_snumberHang up after this many seconds of silence min 10; max 120
volumenumbermin 0; max 2
responsivenessnumberHow fast the agent answers (1 = fastest) min 0; max 1
interruption_sensitivitynumbermin 0; max 1
dynamic_voice_speedboolean
dynamic_responsivenessboolean
expressive_modebooleanOnly works for voices marked expressive in list_voice_catalog
denoising_modestringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsnumberNudge a silent person after N seconds; 0 = off min 0; max 120
handbookobjectBehaviour switches (only the ones you pass change)
handbook.conversational_personalityboolean
handbook.natural_filler_wordsboolean
handbook.high_empathyboolean
handbook.echo_verificationboolean
handbook.speech_normalizationboolean
handbook.ai_disclosureboolean
handbook.scope_boundariesboolean
llmstringModel: fast, balanced (default) or smart one of: fast, balanced, smart
screen_cardsbooleanShow interactive cards (buttons/forms) on screen during voice calls

Calls: PATCH /v1/agents/:id/widget-voice

Channels (connections) 6

create_agent_connectionAdd a channel to an agentnew

Add a channel. WEBSITE_WIDGET needs website_url; new widgets get the app defaults (Modern design) plus your widget_config, then get_widget_embed_code. WEBSITE_BUTTON: just creates the channel (style it in get_widget_embed_code). PRESENTATION_WIDGET = publish the Slide Presenter (optional website_url). GOOGLE_MEET: prefer connect_google_meet. PHONE_CALL: prefer setup_phone_agent. Telegram → connect_telegram_bot; WhatsApp → connect_whatsapp_account. A second widget for the same URL updates the existing one. Free. Interactive cards in chat need design "modern"; in voice they need set_agent_voice screen_cards=true.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
typestringyesone of: WEBSITE_WIDGET, WEBSITE_BUTTON, PRESENTATION_WIDGET, GOOGLE_MEET, PHONE_CALL
namestring
website_urlstringSite where the widget/presentation is installed
widget_configobjectWebsite widget settings (only what you pass changes; texts/theme are merged)
widget_config.designstringmodern = new skin with interactive cards in chat; classic = old look one of: classic, modern
widget_config.themeobjectModern design look (merged with the saved theme)
widget_config.theme.glassboolean
widget_config.theme.glassOpacitynumbermin 0; max 1
widget_config.theme.blurnumbermin 0; max 60
widget_config.theme.headerStylestringone of: gradient, solid
widget_config.theme.gradientstring[]min items 2; max items 3
widget_config.theme.gradientAnglenumbermin 0; max 360
widget_config.theme.accentGradientboolean
widget_config.theme.openAnimationstringone of: genie, spring, scale, slide, fade
widget_config.theme.voiceVisualstringLook of the voice view: portrait, liquid or waves one of: portrait, liquid, waves
widget_config.positionstringone of: bottom-right, bottom-left, top-right, top-left
widget_config.primaryColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.backgroundColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.textColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.borderRadiusnumbermin 0; max 40
widget_config.buttonColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.buttonTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.headerColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.headerTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.userBubbleColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.userBubbleTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.agentBubbleColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.agentBubbleTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.logoVariantstringone of: white, blue, black
widget_config.launcherShapestringone of: round, square
widget_config.launcher_image_urlstringPublic URL of a small square picture for the widget button (PNG/JPG/WebP/GIF/SVG, ≤ ~110 KB)
widget_config.launcherImage"""" = remove the custom button picture (Vendo logo again)
widget_config.collectNameboolean
widget_config.collectEmailboolean
widget_config.collectPhoneboolean
widget_config.teaserbooleanInvitation: show autoOpenMessage as a bubble next to the closed button
widget_config.teaserDelaynumbermin 3; max 30
widget_config.autoOpenbooleanOpen the widget automatically after autoOpenDelay seconds
widget_config.autoOpenDelaynumbermin 0; max 600
widget_config.autoOpenSoundboolean
widget_config.autoOpenMessagestringmax length 500
widget_config.replySoundboolean
widget_config.unreadBadgeboolean
widget_config.navigationEnabledbooleanLet the agent open pages of the site
widget_config.navigationModestringone of: auto, ask
widget_config.continueAfterNavigateboolean
widget_config.textsobjectWording overrides; keys: welcomeMessage, welcomeFollowUp (greeting bubbles) and the contact form: contactTitle, contactSubtitle, nameLabel, namePlaceholder, nameRequired, emailLabel, emailPlaceholder, emailRequired, emailInvalid, phoneLabelRequired, phoneLabel, phonePlaceholder, phoneRequired, phoneInvalid, contactSubmit. "" or null = back to the default (auto-translated). Merged with saved texts.
google_meet_configobject
google_meet_config.auto_joinboolean
google_meet_config.join_before_minutesnumberJoin N minutes before start (default 1) min 0; max 30
google_meet_config.greeting_messagestringmax length 1000
google_meet_config.languagestringMeeting language code, e.g. "en"
google_meet_config.meeting_modestringpresentation = shares the agent's slides (needs slides) one of: voice_only, presentation
phone_call_configobjectVoice Call texts only; number, voice and inbound are set with setup_phone_agent
phone_call_config.call_scriptstring
phone_call_config.greeting_messagestring
phone_call_config.call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
phone_call_config.max_call_duration_secondsnumbermin 30; max 1800
phone_call_config.post_call_actionsobject
phone_call_config.post_call_actions.log_outcomeboolean
phone_call_config.post_call_actions.send_followup_emailboolean
phone_call_config.post_call_actions.book_meeting_if_interestedboolean
phone_call_config.post_call_actions.collect_contact_infoboolean

Calls: POST /v1/agents/:id/connections

delete_agent_connectionRemove a channel (irreversible)newexecute

Remove a channel from the agent (the embed code stops working). No undo. Only after preview_delete_agent_connection and explicit user approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: DELETE /v1/agents/:id/connections/:cid

get_agent_connectionGet one channel of an agentnew

Full settings of one channel (website widget, button, presentation, Google Meet, phone, Telegram). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: GET /v1/agents/:id/connections/:cid

list_agent_connectionsList an agent's connections (channels)

See every channel attached to this agent: widgets, telegram, phone, presentations, etc.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/agents/:id/connections

preview_delete_agent_connectionPreview removing a channelnewpreview

Show what removing a channel would do. Nothing is removed. ALWAYS call first and show the user before delete_agent_connection.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: DELETE /v1/agents/:id/connections/:cid

update_agent_connectionChange a channel's settingsnew

Change a channel: website widget look and behaviour (design, colours, button picture, invitation/auto-open, sounds, contact form and its wording, greetings, page navigation, voice view), name, website_url, Google Meet settings or phone texts. Only what you pass changes; widget texts and theme are merged. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections
namestring
website_urlstring
widget_configobjectWebsite widget settings (only what you pass changes; texts/theme are merged)
widget_config.designstringmodern = new skin with interactive cards in chat; classic = old look one of: classic, modern
widget_config.themeobjectModern design look (merged with the saved theme)
widget_config.theme.glassboolean
widget_config.theme.glassOpacitynumbermin 0; max 1
widget_config.theme.blurnumbermin 0; max 60
widget_config.theme.headerStylestringone of: gradient, solid
widget_config.theme.gradientstring[]min items 2; max items 3
widget_config.theme.gradientAnglenumbermin 0; max 360
widget_config.theme.accentGradientboolean
widget_config.theme.openAnimationstringone of: genie, spring, scale, slide, fade
widget_config.theme.voiceVisualstringLook of the voice view: portrait, liquid or waves one of: portrait, liquid, waves
widget_config.positionstringone of: bottom-right, bottom-left, top-right, top-left
widget_config.primaryColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.backgroundColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.textColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.borderRadiusnumbermin 0; max 40
widget_config.buttonColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.buttonTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.headerColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.headerTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.userBubbleColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.userBubbleTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.agentBubbleColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.agentBubbleTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
widget_config.logoVariantstringone of: white, blue, black
widget_config.launcherShapestringone of: round, square
widget_config.launcher_image_urlstringPublic URL of a small square picture for the widget button (PNG/JPG/WebP/GIF/SVG, ≤ ~110 KB)
widget_config.launcherImage"""" = remove the custom button picture (Vendo logo again)
widget_config.collectNameboolean
widget_config.collectEmailboolean
widget_config.collectPhoneboolean
widget_config.teaserbooleanInvitation: show autoOpenMessage as a bubble next to the closed button
widget_config.teaserDelaynumbermin 3; max 30
widget_config.autoOpenbooleanOpen the widget automatically after autoOpenDelay seconds
widget_config.autoOpenDelaynumbermin 0; max 600
widget_config.autoOpenSoundboolean
widget_config.autoOpenMessagestringmax length 500
widget_config.replySoundboolean
widget_config.unreadBadgeboolean
widget_config.navigationEnabledbooleanLet the agent open pages of the site
widget_config.navigationModestringone of: auto, ask
widget_config.continueAfterNavigateboolean
widget_config.textsobjectWording overrides; keys: welcomeMessage, welcomeFollowUp (greeting bubbles) and the contact form: contactTitle, contactSubtitle, nameLabel, namePlaceholder, nameRequired, emailLabel, emailPlaceholder, emailRequired, emailInvalid, phoneLabelRequired, phoneLabel, phonePlaceholder, phoneRequired, phoneInvalid, contactSubmit. "" or null = back to the default (auto-translated). Merged with saved texts.
google_meet_configobject
google_meet_config.auto_joinboolean
google_meet_config.join_before_minutesnumberJoin N minutes before start (default 1) min 0; max 30
google_meet_config.greeting_messagestringmax length 1000
google_meet_config.languagestringMeeting language code, e.g. "en"
google_meet_config.meeting_modestringpresentation = shares the agent's slides (needs slides) one of: voice_only, presentation
phone_call_configobjectVoice Call texts only; number, voice and inbound are set with setup_phone_agent
phone_call_config.call_scriptstring
phone_call_config.greeting_messagestring
phone_call_config.call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
phone_call_config.max_call_duration_secondsnumbermin 30; max 1800
phone_call_config.post_call_actionsobject
phone_call_config.post_call_actions.log_outcomeboolean
phone_call_config.post_call_actions.send_followup_emailboolean
phone_call_config.post_call_actions.book_meeting_if_interestedboolean
phone_call_config.post_call_actions.collect_contact_infoboolean

Calls: PATCH /v1/agents/:id/connections/:cid

Website widget & button 2

create_widget_test_pageCreate a test page for a widgetnew

Create (or return the existing) private, unlisted test page with this website widget installed, to try it before adding it to a real site. Only the agent's creator can do this. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: POST /v1/agents/:id/connections/:cid/test-page

get_widget_embed_codeGet the embed code of a channelupdated

HTML to paste on a site for a channel: website widget (settings load from the server), website button (style it here: text, colours, radius, size, not stored server-side) or Slide Presenter (also the direct link). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections
textstringButton only: label (default "Talk to Assistant")
background_colorstringButton only pattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
text_colorstringButton only pattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
border_radiusnumberButton only (px) min 0; max 50
sizestringButton only one of: small, medium, large
modal_primary_colorstringButton only: colour of the chat window it opens pattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$

Calls: GET /v1/agents/:id/connections/:cid/embed-code

Slide Presenter 14

delete_narrator_leadDelete a presentation lead (irreversible)newexecute

Permanently delete a presentation lead. Only after preview_delete_narrator_lead and explicit user approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
lead_idstringyes

Calls: DELETE /v1/agents/:id/narrator/leads/:leadId

generate_slide_scriptWrite the presenter script for a slidenew

AI-write what the presenter says on one slide, in the presentation's language. 1 credit (2 with image_url, the AI looks at the slide). NOT saved, put it into narrator_script with set_presentation_slides.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
slide_numberintegeryesmin 1
total_slidesintegeryesmin 1
extracted_textstringText on the slide
previous_scriptstring
next_slide_textstring
image_urlstringPublic URL of the slide image (optional, 2 credits)

Calls: POST /v1/agents/:id/generate-narrator-script

generate_transition_phrasesGenerate slide transition phrasesnew

Generate the phrases the Slide Presenter uses to move between slides, in the presentation's language, and save them on the agent. Needs saved slides (set_presentation_slides).

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: POST /v1/agents/:id/generate-transition-phrases

get_narrator_analyticsPresentation analyticsupdated

Slide Presenter analytics. view "summary" (default): views, presentations started, watch time, unique viewers, average duration, completion rate, recent sessions, per channel. view "period" with date_from/date_to: widget loads, starts, watch minutes, unique viewers, credits used. Read-only. (No per-slide drop-off.)

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
viewstringone of: summary, period
date_fromstringFrom (YYYY-MM-DD or ISO 8601)
date_tostringTo (YYYY-MM-DD or ISO 8601)

Calls: GET /v1/agents/:id/narrator/analytics

get_presentationGet the agent's Slide Presenternew

The agent's presentation: PDF name, slides (text + presenter script; images with include_images), look, call-to-action, presenter voice, published channels and direct link. Read-only. (list_agent_presentations shows only the legacy creator.)

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
include_imagesbooleanAdd slide image URLs

Calls: GET /v1/agents/:id/presentation

get_presentation_embed_codeGet the presentation embed code & linknew

Direct link (narrator page) and the HTML snippet with the saved look. Publish it as a channel first (create_agent_connection type PRESENTATION_WIDGET). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: GET /v1/agents/:id/presentation/embed

list_agent_presentationsGet the agent's presentation (alias)updated

Alias of get_presentation kept for older clients: returns the agent's current Slide Presenter deck (PDF name, slides with text and presenter script, look, call-to-action, voice, published channels, direct link), slides_count 0 when no deck is uploaded. Prefer get_presentation. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
include_imagesbooleanAdd slide image URLs

Calls: GET /v1/agents/:id/presentations

list_narrator_leadsList presentation leadsupdated

Leads from the presentation call-to-action form (form data, page, status, notes). Filter by status, page with limit/offset. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
statusstringone of: new, contacted, converted, spam
limitintegermin 1; max 500
offsetintegermin 0

Calls: GET /v1/agents/:id/narrator/leads

preview_delete_narrator_leadPreview deleting a presentation leadnewpreview

Show the lead that would be deleted. Nothing is deleted. Call first and show the user before delete_narrator_lead.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
lead_idstringyes

Calls: DELETE /v1/agents/:id/narrator/leads/:leadId

set_presentation_ctaSet the presentation call-to-actionnew

The button viewers see: a lead form (fields) or a link. Only what you pass changes (fields replaces the whole field list). Leads land in list_narrator_leads. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
enabledboolean
buttonTextstring
typestringone of: form, link
linkUrlstring
linkOpenInNewTabboolean
successMessagestring
fieldsobject[]max items 12
fields[].idstring
fields[].typestringyesone of: text, email, phone, textarea, select
fields[].labelstringyes
fields[].placeholderstring
fields[].requiredboolean
fields[].optionsstring[]

Calls: PATCH /v1/agents/:id/presentation/cta

set_presentation_lookSet the presentation look & behaviournew

Theme, colours, corner radius, controls, autoplay, voice on/off, chat on/off, Modern look (background, glass, font, panel side, presenter bubble, voice visual), slide transition and button texts. Only what you pass changes. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
themestringone of: dark, light, custom
backgroundColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
buttonColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
buttonTextColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
progressColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
textColorstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
borderRadiusnumbermin 0; max 40
showControlsboolean
autoplayboolean
voiceEnabledbooleanViewers can listen/talk by voice
chatEnabledbooleanViewers can chat
designstringone of: classic, modern
modernobject
modern.backgroundstringone of: solid, gradient
modern.bgFromstringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
modern.bgTostringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
modern.anglenumbermin 0; max 360
modern.glassboolean
modern.glassOpacitynumbermin 0; max 1
modern.blurnumbermin 0; max 60
modern.gradientButtonsboolean
modern.accent2stringpattern ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
modern.panelSidestringone of: right, left
modern.presenterBubblestringone of: off, left, right
modern.fontstringone of: system, rounded, serif, mono
modern.glowboolean
modern.voiceVisualstringone of: portrait, liquid, waves
transitionstringone of: zoom, fade, slide
textsobject"" or null = default text
texts.chooseModestring | null
texts.voicestring | null
texts.chatstring | null
texts.placeholderstring | null
texts.clickToStartstring | null

Calls: PATCH /v1/agents/:id/presentation/look

set_presentation_slidesSave the presentation slidesnew

Save the slide deck, REPLACES all slides (add, remove, reorder, edit). Each slide: image (public URL or base64; JPG/PNG/WebP/GIF ≤5 MB; omit to keep the stored image of an existing slide id), extracted text and presenter script. PDF upload is not supported by the API: render the PDF pages to images first. Then write scripts with generate_slide_script and set_presentation_voice. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
pdf_namestringName of the presentation, e.g. "Pitch deck.pdf"
slidesobject[]yesmin items 1; max items 100
slides[].idstringKeep an existing slide id to keep its stored image
slides[].page_numbernumber
slides[].image_urlstring
slides[].image_base64string
slides[].extracted_textstring
slides[].narrator_scriptstringWhat the presenter says on this slide

Calls: POST /v1/agents/:id/presentation/slides

set_presentation_voiceSet the presenter's voicenew

The Slide Presenter's own voice and model (the website widget voice is not touched). Same settings as the app; only what you pass changes. Needs saved slides. Takes up to ~1 min. Free to configure; presentation voice costs 17 credits/min.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
voice_idstringVoice id from list_voice_catalog (e.g. "retell-Grace", "11labs-Adrian")
speednumberSpeaking pace, 0.7-1.3 (1 = normal) min 0.7; max 1.3
emotionstringDelivery: neutral, expressive (warm), calm, dynamic (energetic) one of: neutral, expressive, calm, dynamic
languagesstring[]Retell locales like ["en-US","uk-UA"]; more than one = multilingual (answers in the caller's language). [] or one locale = script language only max items 20
rulesstringCommunication rules (plain text instructions for how the agent talks). Omit to keep the saved ones max length 8000
ambientstringBackground sound; "" = none one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumenumbermin 0; max 2
backchannelbooleanShort "mm-hm" acknowledgements while the person talks
backchannel_frequencynumbermin 0; max 1
end_call_snumberHang up after this many seconds of silence min 10; max 120
volumenumbermin 0; max 2
responsivenessnumberHow fast the agent answers (1 = fastest) min 0; max 1
interruption_sensitivitynumbermin 0; max 1
dynamic_voice_speedboolean
dynamic_responsivenessboolean
expressive_modebooleanOnly works for voices marked expressive in list_voice_catalog
denoising_modestringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsnumberNudge a silent person after N seconds; 0 = off min 0; max 120
handbookobjectBehaviour switches (only the ones you pass change)
handbook.conversational_personalityboolean
handbook.natural_filler_wordsboolean
handbook.high_empathyboolean
handbook.echo_verificationboolean
handbook.speech_normalizationboolean
handbook.ai_disclosureboolean
handbook.scope_boundariesboolean
llmstringModel: fast, balanced (default) or smart one of: fast, balanced, smart
screen_cardsbooleanShow interactive cards (buttons/forms) on screen during voice calls

Calls: PATCH /v1/agents/:id/presentation/voice

update_narrator_leadUpdate a presentation leadnew

Set a presentation lead's status (new / contacted / converted / spam) and notes. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
lead_idstringyes
statusstringone of: new, contacted, converted, spam
notesstring

Calls: PATCH /v1/agents/:id/narrator/leads/:leadId

Google Meet 8

connect_google_meetConnect Google Meet (invite address)updated

Turn on the Google Meet channel the way the app does: returns the agent's own invite address (agent-xxxxxxxx@meet.vendo-ai.com). Invite that address to any Google Calendar event with a Meet link and the agent joins automatically. No Google login needed. Also saves the meeting settings. Free to connect; meeting minutes are billed.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
auto_joinboolean
join_before_minutesnumberJoin N minutes before start (default 1) min 0; max 30
greeting_messagestringmax length 1000
languagestringMeeting language code, e.g. "en"
meeting_modestringpresentation = shares the agent's slides (needs slides) one of: voice_only, presentation

Calls: POST /v1/agents/:id/google-meet/connect

disconnect_google_meetDisconnect Google Meetupdatedexecute

Remove the Google Meet channel from the agent and turn auto-join off. Already scheduled meetings are not cancelled (use cancel_meeting). Only after preview_disconnect_google_meet and user approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: DELETE /v1/agents/:id/google-meet

get_google_meet_analyticsGoogle Meet analytics of an agentnew

Meetings, minutes, completed, missed and credits used for a date range. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
date_fromstringFrom (YYYY-MM-DD or ISO 8601)
date_tostringTo (YYYY-MM-DD or ISO 8601)

Calls: GET /v1/agents/:id/google-meet/analytics

get_google_meet_settingsGet Google Meet status & settingsnew

Whether Google Meet is connected for this agent, its invite address, settings and totals. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: GET /v1/agents/:id/google-meet

list_google_meet_historyMeeting history of an agentnew

All meetings of this agent (newest first) with status, times, duration, whether a transcript exists. Paged. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
pageintegermin 1
limitintegermin 1; max 200

Calls: GET /v1/agents/:id/google-meet/meetings/history

list_google_meet_meetingsList Google Meet meetings booked by this agent

Read-only list of meetings the agent has scheduled. Requires Google Meet connected first.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/agents/:id/google-meet/meetings

preview_disconnect_google_meetPreview disconnecting Google Meetnewpreview

Show what disconnecting Google Meet would do. Nothing changes. Call first and show the user.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: DELETE /v1/agents/:id/google-meet

update_google_meet_settingsChange Google Meet settingsnew

Auto-join, minutes before start, greeting, language, meeting mode (voice only / presentation, needs slides). Only what you pass changes. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
auto_joinboolean
join_before_minutesnumberJoin N minutes before start (default 1) min 0; max 30
greeting_messagestringmax length 1000
languagestringMeeting language code, e.g. "en"
meeting_modestringpresentation = shares the agent's slides (needs slides) one of: voice_only, presentation

Calls: PATCH /v1/agents/:id/google-meet/config

Telegram 12

connect_telegram_botConnect a Telegram bot to the agent

The user creates a bot with @BotFather, gets the HTTP API token, and passes it here. After that, all messages to the bot are answered by the agent.

ArgumentTypeReq.Description
idstringyes
bot_tokenstringyesTelegram Bot API token from @BotFather

Calls: POST /v1/agents/:id/telegram/connect

disconnect_telegram_botDisconnect Telegram botupdatedexecute

Detach the bot from this agent: the webhook is removed and the bot stops answering (it keeps existing in Telegram; chat history is kept; reconnect with connect_telegram_bot). Only after preview_disconnect_telegram_bot and the user's approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: DELETE /v1/agents/:id/telegram

get_telegram_analyticsTelegram analyticsupdated

Chats, messages, voice calls, text messages, unique users and credits (text/voice) of the agent's Telegram bot for a date range. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
date_fromstringFrom (YYYY-MM-DD or ISO 8601)
date_tostringTo (YYYY-MM-DD or ISO 8601)

Calls: GET /v1/agents/:id/telegram/analytics

get_telegram_bot_infoGet current Telegram bot info

See the connected bot username + settings.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/agents/:id/telegram

get_telegram_chatRead a Telegram chatnew

All messages of one Telegram chat, incl. voice-call turns with recording URL and duration. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
chat_idstringyeschats[].id from list_telegram_chats

Calls: GET /v1/agents/:id/telegram/chats/:chatId

list_telegram_chatsList Telegram chatsnew

People/groups talking to the agent's Telegram bot: name, username, type, message count, last message time. Paged. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
pageintegermin 1
limitintegermin 1; max 200

Calls: GET /v1/agents/:id/telegram/chats

preview_disconnect_telegram_botPreview disconnecting the Telegram botnewpreview

Show which Telegram bot would be detached from the agent (username, chats, messages). Nothing changes. Call first.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: DELETE /v1/agents/:id/telegram

preview_send_telegram_messagePreview an operator Telegram messagenewpreview

Show the recipient and exact text of an operator message. Nothing is sent. ALWAYS call first and show the user the text.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
chat_idstringyes
messagestringyesmax length 4096

Calls: POST /v1/agents/:id/telegram/chats/:chatId/send

reconfigure_telegram_botRe-apply Telegram bot commandsnew

Re-apply the bot's commands and menu button (for private and group chats), use when the bot menu looks wrong. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents

Calls: POST /v1/agents/:id/telegram/reconfigure

send_telegram_messageSend a Telegram message as the operatornewexecute

Sends a REAL Telegram message from the bot to this person (human takeover). Only after preview_send_telegram_message and the user approved the exact text.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
chat_idstringyes
messagestringyesmax length 4096

Calls: POST /v1/agents/:id/telegram/chats/:chatId/send

set_telegram_voice_callsTurn Telegram voice calls on/offnew

Show or hide the VOICE button of the agent's Telegram bot (voice calls in the mini app). Needs a connected bot. Free.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
enabledbooleanyes

Calls: PATCH /v1/agents/:id/telegram/voice

set_telegram_voice_settingsSet the Telegram voicenew

Voice and behaviour of the Telegram bot's voice calls (separate from the website widget): the same settings as set_agent_voice plus the mini app look (visual). Only what you pass changes. Needs a connected bot. Takes up to ~1 min. Free to configure; calls are billed per minute.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
voice_idstringVoice id from list_voice_catalog (e.g. "retell-Grace", "11labs-Adrian")
speednumberSpeaking pace, 0.7-1.3 (1 = normal) min 0.7; max 1.3
emotionstringDelivery: neutral, expressive (warm), calm, dynamic (energetic) one of: neutral, expressive, calm, dynamic
languagesstring[]Retell locales like ["en-US","uk-UA"]; more than one = multilingual (answers in the caller's language). [] or one locale = script language only max items 20
rulesstringCommunication rules (plain text instructions for how the agent talks). Omit to keep the saved ones max length 8000
ambientstringBackground sound; "" = none one of: "", coffee-shop, call-center, convention-hall
ambient_sound_volumenumbermin 0; max 2
backchannelbooleanShort "mm-hm" acknowledgements while the person talks
backchannel_frequencynumbermin 0; max 1
end_call_snumberHang up after this many seconds of silence min 10; max 120
volumenumbermin 0; max 2
responsivenessnumberHow fast the agent answers (1 = fastest) min 0; max 1
interruption_sensitivitynumbermin 0; max 1
dynamic_voice_speedboolean
dynamic_responsivenessboolean
expressive_modebooleanOnly works for voices marked expressive in list_voice_catalog
denoising_modestringone of: noise-cancellation, noise-and-background-speech-cancellation
reminder_secondsnumberNudge a silent person after N seconds; 0 = off min 0; max 120
handbookobjectBehaviour switches (only the ones you pass change)
handbook.conversational_personalityboolean
handbook.natural_filler_wordsboolean
handbook.high_empathyboolean
handbook.echo_verificationboolean
handbook.speech_normalizationboolean
handbook.ai_disclosureboolean
handbook.scope_boundariesboolean
llmstringModel: fast, balanced (default) or smart one of: fast, balanced, smart
screen_cardsbooleanShow interactive cards (buttons/forms) on screen during voice calls
visualstringLook of the mini app during a call one of: portrait, liquid, waves

Calls: PATCH /v1/agents/:id/telegram/voice-settings

Voice Call (phone) 8

generate_phone_scriptDraft a phone call scriptnew

Draft a call script and greeting from the agent's sales script for a call objective, in the agent's language. Free. Not saved, pass it to setup_phone_agent (or let setup_phone_agent generate it).

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
voice_genderstringone of: male, female

Calls: POST /v1/agents/:id/phone/generate-script

get_phone_channel_statsVoice Call channel countersnew

Calls attempted/completed/successful, minutes and credits of one Voice Call channel. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: GET /v1/agents/:id/connections/:cid/phone-stats

list_phone_callsList the agent's phone callsnew

Phone calls of this agent (incl. test and inbound) with transcript, recording URL, summary, outcome, sentiment, duration and credits, plus totals by outcome/sentiment. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
limitintegermin 1; max 500

Calls: GET /v1/agents/:id/phone/calls

list_phone_numbersList my phone numbersnew

Phone numbers already on your account (number, country, type, monthly cost, per-minute rate). Buying numbers is done in the app. Read-only.

No arguments.

Calls: GET /v1/phone/numbers

phone_test_callMake a test call (real phone)newexecute

DIALS A REAL PHONE: the agent calls to_number from the channel's number (max 2 min, 25 credits/min). Only call the user's own number, only after preview_phone_test_call and explicit approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
to_numberstringyes
connection_idstring

Calls: POST /v1/agents/:id/phone/test-call

preview_phone_test_callPreview a test callnewpreview

Show which number would be called from which, and the cost (25 credits/min, max 2 min). Nothing is dialled. ALWAYS call first.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
to_numberstringyesE.164, e.g. +447700900123
connection_idstring

Calls: POST /v1/agents/:id/phone/test-call

preview_setup_phone_agentPreview the Voice Call setupnewpreview

Show what setup_phone_agent would do with the same arguments: which channel is created or updated, which of YOUR numbers is attached, objective, voice, inbound on/off. Nothing is attached or created. ALWAYS call first and show the user, then call setup_phone_agent with the same arguments after approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
phone_number_idstringRequired the first time, from list_phone_numbers
connection_idstringExisting PHONE_CALL channel; default = the agent's first one or a new one
call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
call_scriptstring
greeting_messagestring
voice_idstringFrom list_retell_voices / list_voice_catalog
speednumbermin 0.7; max 1.3
emotionstringone of: neutral, expressive, calm, dynamic
languagestringauto = script language; multi = switch to the caller's language one of: auto, multi
communication_rulesstring
voice_genderstringone of: male, female
accept_incomingbooleanAnswer calls TO this number with an inbound agent
ambient_soundstringone of: "", coffee-shop, call-center, convention-hall
enable_backchannelboolean
end_call_silence_secondsnumbermin 10; max 120
reminder_trigger_secondsnumbermin 5; max 60
max_call_duration_secondsnumbermin 30; max 1800

Calls: POST /v1/agents/:id/phone/setup

setup_phone_agentSet up the Voice Call channelupdatedexecute

Create/update the agent's Voice Call channel like the app: attach one of YOUR numbers (list_phone_numbers), objective, script (generated if omitted), greeting, voice, pace, emotion, language, rules, inbound calls on/off, background sound, silence/reminder timings. Creates the phone voice agent at the provider and links the number (with accept_incoming the number starts answering real calls). Takes up to ~1 min. Free to set up; calls cost 25 credits/min. Only call after preview_setup_phone_agent and the user's approval.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
phone_number_idstringRequired the first time, from list_phone_numbers
connection_idstringExisting PHONE_CALL channel; default = the agent's first one or a new one
call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
call_scriptstring
greeting_messagestring
voice_idstringFrom list_retell_voices / list_voice_catalog
speednumbermin 0.7; max 1.3
emotionstringone of: neutral, expressive, calm, dynamic
languagestringauto = script language; multi = switch to the caller's language one of: auto, multi
communication_rulesstring
voice_genderstringone of: male, female
accept_incomingbooleanAnswer calls TO this number with an inbound agent
ambient_soundstringone of: "", coffee-shop, call-center, convention-hall
enable_backchannelboolean
end_call_silence_secondsnumbermin 10; max 120
reminder_trigger_secondsnumbermin 5; max 60
max_call_duration_secondsnumbermin 30; max 1800

Calls: POST /v1/agents/:id/phone/setup

WhatsApp 5

connect_whatsapp_accountGet a WhatsApp connect linkupdatedexecute

Returns a hosted link; the user opens it and scans the QR code with WhatsApp (Linked devices), that links a REAL phone number to the workspace. Poll list_whatsapp_accounts until status is "ok". Free. Only after preview_connect_whatsapp_account and the user's approval.

No arguments.

Calls: POST /v1/whatsapp/accounts/connect

disconnect_whatsapp_accountDisconnect WhatsApp (irreversible)newexecute

Disconnect and delete the WhatsApp account; auto-replies stop and reconnecting needs a new QR scan. Only after preview_disconnect_whatsapp_account and user approval.

ArgumentTypeReq.Description
account_idstringyes

Calls: DELETE /v1/whatsapp/accounts/:accountId

list_whatsapp_accountsList WhatsApp accountsnew

WhatsApp accounts connected to your user (phone, name, status: connecting / ok / error / credentials / stopped). Account-level: auto-replies use your latest campaign's agent. Read-only.

No arguments.

Calls: GET /v1/whatsapp/accounts

preview_connect_whatsapp_accountPreview connecting WhatsAppnewpreview

Explain what connecting a WhatsApp number does and list the accounts already connected. No link is created. Call first, then connect_whatsapp_account after the user approves.

No arguments.

Calls: POST /v1/whatsapp/accounts/connect

preview_disconnect_whatsapp_accountPreview disconnecting WhatsAppnewpreview

Show the WhatsApp account that would be disconnected. Nothing changes. Call first.

ArgumentTypeReq.Description
account_idstringyes

Calls: DELETE /v1/whatsapp/accounts/:accountId

Conversations & recordings 6

get_conversation_recordingsGet voice recordings of a conversationnew

Recording URLs (WAV), duration and start time of the voice calls in one widget conversation (up to 10 calls). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections
conversation_idstringyesFrom list_connection_conversations

Calls: GET /v1/agents/:id/connections/:cid/conversations/:conversationId/recording

get_linkedin_conversationOpen a LinkedIn conversationnew

All messages of one LinkedIn chat (chat_id from list_linkedin_conversations). Read-only.

ArgumentTypeReq.Description
chat_idstringyes

Calls: GET /v1/linkedin/conversations/:chatId/messages

list_connection_conversationsList conversations of a channelnew

Conversations of a widget/presentation channel with messages, mode (chat/voice), visitor name/email/phone and page. Paged (limit ≤500). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections
pageintegermin 1
limitintegermin 1; max 500

Calls: GET /v1/agents/:id/connections/:cid/conversations

list_linkedin_conversationsLinkedIn conversationsnew

LinkedIn chats of the workspace (optionally of one campaign and/or account): contact, last message, pending AI reply, human takeover flag, outreach status. Read-only.

ArgumentTypeReq.Description
campaign_idstring
account_idstring
limitintegermin 1; max 100
offsetintegermin 0

Calls: GET /v1/linkedin/conversations

preview_send_linkedin_messagePreview a manual LinkedIn messagenewpreview

Dry-run: recipient, exact text, cost (5 credits) and whether the balance suffices. Nothing is sent. Call before send_linkedin_message and show the text to the user.

ArgumentTypeReq.Description
chat_idstringyes
textstringyes

Calls: POST /v1/linkedin/conversations/:chatId/messages

send_linkedin_messageSend a LinkedIn message (REAL, 5 credits)newexecute

Send a REAL LinkedIn message in the chat; costs 5 credits, the AI stops messaging this contact (human takeover) and the CRM lead moves to Manager Dialog. Only after the user approved preview_send_linkedin_message.

ArgumentTypeReq.Description
chat_idstringyes
textstringyes

Calls: POST /v1/linkedin/conversations/:chatId/messages

Message previews 3

preview_agent_emailPreview an email the agent would writeupdatedexecute

Render a sample outreach email from an agent, no sending, no credits. Optional mode/prompt/tone/customInstructions/variant/followUp{index,delayDays,previous}/sample make it render exactly like a campaign would (same as preview_campaign_email).

ArgumentTypeReq.Description
idstringyesAgent id

Calls: POST /v1/agents/:id/preview/email

preview_linkedin_followupPreview a LinkedIn follow-up the agent would writeexecute

Render a sample first follow-up (after the connection is accepted). No sending, no cost.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/agents/:id/preview/linkedin-followup

preview_linkedin_invitePreview a LinkedIn invite the agent would writeexecute

Render a sample LinkedIn connection request from this agent. No sending, no cost.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/agents/:id/preview/linkedin-invite

Agent analytics & credits 4

get_agent_connection_statsChannel countersnew

Conversations, messages, unique visitors, last activity of one channel. For widgets prefer get_connection_analytics (fresher). Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections

Calls: GET /v1/agents/:id/connections/:cid/stats

get_agents_dashboardLegacy agents dashboard numbersnew

Older overview numbers of all agents (superseded by get_analytics_agents). Read-only.

No arguments.

Calls: GET /v1/agents/dashboard/analytics

get_connection_analyticsWidget/button channel analyticsnew

For a website widget/button/presentation channel: widget loaded, activated, chats started, voice calls started, plus estimated credits (text messages, voice minutes) for a date range. Read-only.

ArgumentTypeReq.Description
idstringyesAgent id (UUID), from list_agents
connection_idstringyesChannel (connection) id (UUID), from list_agent_connections
date_fromstringFrom (YYYY-MM-DD or ISO 8601)
date_tostringTo (YYYY-MM-DD or ISO 8601)

Calls: GET /v1/agents/:id/connections/:cid/analytics

list_agents_with_statsList agents with usage stats

Same as list_agents but includes per-agent stats (connections, credits spent, status).

No arguments.

Calls: GET /v1/agents/with-stats

Meetings tools 8

Meetings calendar 8

cancel_meetingCancel a meeting for the agentnewexecute

The agent will NOT join this scheduled meeting (the calendar event itself stays). Only scheduled, not yet started meetings. No undo. Only after preview_cancel_meeting and user approval.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: POST /v1/meetings/:meetingId/cancel

get_meetingGet a meetingnew

One meeting with transcript, summary, action items, join/leave time and duration. Read-only.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: GET /v1/meetings/:meetingId

get_meeting_transcriptGet a meeting transcriptnew

Transcript messages, summary and action items of a meeting (from the database or the recording service). Read-only.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: GET /v1/meetings/:meetingId/transcript

list_meeting_agentsAgents with Google Meetnew

Agents that have Google Meet connected (the calendar's agent filter). Read-only.

No arguments.

Calls: GET /v1/meetings/agents

list_meetingsMeetings calendar (all agents)new

Google Meet meetings of all your agents in a date range (default: next 7 days, max 62 days), title, time, link, organizer, attendees, status, agent, has transcript. Read-only.

ArgumentTypeReq.Description
startstringISO date/time
endstringISO date/time
agent_idstring

Calls: GET /v1/meetings

preview_cancel_meetingPreview cancelling a meeting for the agentnewpreview

Show which meeting the agent would skip. Nothing changes. Call first and show the user.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: POST /v1/meetings/:meetingId/cancel

preview_remove_agent_from_meetingPreview removing the agent from a live callnewpreview

Show whether the agent is in this live meeting and what removing it does. Nothing changes.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: POST /v1/meetings/:meetingId/remove-agent

remove_agent_from_meetingRemove the agent from a live callnewexecute

The agent leaves the live Google Meet call right now. Only works while the meeting is in progress. Only after preview_remove_agent_from_meeting and user approval.

ArgumentTypeReq.Description
meeting_idstringyes

Calls: POST /v1/meetings/:meetingId/remove-agent

Vendo SDR tools 75

Overview & live signals 4

sdr_company_heatHot companiesupdated

Read-only. Companies ranked by live signal heat, with ICP fit, signal types and contact status. One search (search_id, pages via offset up to any depth) or all searches merged (first 500 rows, offset + limit ≤ 500).

ArgumentTypeReq.Description
search_idstring (uuid)
limitintegermin 1; max 500
offsetintegermin 0

Calls: GET /v1/sdr/company-heat

sdr_learning_insightsSignal Learning insightsupdated

Read-only. Learned weight per signal type and conversion from real outreach outcomes, for one search or per search across the workspace.

ArgumentTypeReq.Description
search_idstring (uuid)

Calls: GET /v1/sdr/learning-insights

sdr_overviewSDR processes overviewnew

Read-only. Every SDR search of the workspace with signals (7/30 days), contacts, hot companies, hunter health (running/cooling/paused) and outreach, plus workspace totals and a daily series, the /vendo-sdr list page.

ArgumentTypeReq.Description
daysintegerDaily series length, default 14 min 7; max 60

Calls: GET /v1/sdr/overview

sdr_recent_signalsRecent signalsupdated

Read-only. Latest signals of one search (search_id) or merged across all searches of the workspace.

ArgumentTypeReq.Description
search_idstring (uuid)
limitintegermin 1; max 100

Calls: GET /v1/sdr/recent

Wizard & ICP research 9

sdr_find_citiesFind cities for an SDR searchnew

Read-only. Main cities of the given countries (no q), or a name search (q ≥ 2 Latin letters). Use the returned objects as target_cities in sdr_create_search / sdr_update_search.

ArgumentTypeReq.Description
countriesstring[]yesISO-2 or CC:SUB codes, e.g. ['GB','US:FL'] min items 1; max items 40
qstring
limitintegerPer country without q (max 30); total with q (max 50) min 1; max 50

Calls: GET /v1/sdr/geo/cities

sdr_generate_agent_signal_profileGenerate an agent's signal profile (Claude)

Run Claude on the agent's knowledge to (re)generate its signal profile. Persists onto the agent so later Signal Searches reuse it.

ArgumentTypeReq.Description
agent_idstringyes

Calls: POST /v1/sdr/agents/:id/profile/generate

sdr_get_agent_signal_profileGet an agent's signal profile

Read the agent's current signal profile (ICP, offer summary, hunter weights). Returns { exists: false } when nothing's generated yet.

ArgumentTypeReq.Description
agent_idstringyes

Calls: GET /v1/sdr/agents/:id/profile

sdr_get_researchPoll an SDR ICP researchnew

Read-only. Pass job_id (from sdr_start_research / a previous poll) or agent_id (while the company profile is still being built, or to get the latest research of a company). Returns status (queued/running/done/error), stages, events and, when done, the result: company dossier and the editable profile (search name, offer, ICP, decision roles, regions, keywords, LinkedIn queries, competitor pages, hunter plan). Poll every 5–10 s.

ArgumentTypeReq.Description
job_idstring (uuid)
agent_idstring (uuid)

Calls: GET /v1/sdr/research/:id, GET /v1/sdr/research

sdr_get_research_audienceFirst buyers found by an ICP researchnew

Read-only. The instant audience of a finished research: up to ~20 matching people from the Vendo database with company, position, country, LinkedIn, email and why they fit, the wizard's "buyers already found" cards. refresh=true rebuilds it (slower).

ArgumentTypeReq.Description
job_idstring (uuid)yes
refreshboolean

Calls: GET /v1/sdr/research/:id/audience

sdr_materialize_researchCopy a research's instant audience into a searchnew

Copies the instant audience of a finished research into a search as contacts (background, idempotent, filtered by the search's regions/cities). sdr_create_search with research_job_id already does this after activation; use this only to repeat it. Contacts are billed (5 credits each) like any other contact when used.

ArgumentTypeReq.Description
job_idstring (uuid)yes
search_idstring (uuid)yes

Calls: POST /v1/sdr/research/:id/materialize

sdr_preview_start_researchPreview: research a company website for a new SDRnewpreview

Dry run of the New SDR wizard's first steps. Tells whether a company profile for this website already exists (reused, 0 credits) or must be built (deep website scan 15 + outreach playbook 1 = 16 credits), and whether a recent ICP research (< 24 h) would be reused. Nothing is created. Always call before sdr_start_research and show the cost.

ArgumentTypeReq.Description
company_namestringyes
websitestringyesDomain or URL, e.g. acme.com
forcebooleanRun a fresh research even if a recent one exists

Calls: POST /v1/sdr/research

sdr_start_researchResearch a company website (New SDR wizard)newexecute

Starts the New SDR wizard for a company + website, exactly like the web app: reuses the company profile for the same domain or builds a hidden one (website deep scan, playbook; 16 credits), then runs the ICP research (1–3 min, not billed). Returns agent_id and either the research job or a setup state. Poll sdr_get_research until the job status is done, then call sdr_create_search with research_job_id. Only call after the user approved sdr_preview_start_research.

ArgumentTypeReq.Description
company_namestringyes
websitestringyes
forceboolean

Calls: POST /v1/sdr/research

Searches 10

sdr_list_searchesList Vendo SDR Signal Searches

List active Signal Searches in this workspace. Optionally filter by agent.

ArgumentTypeReq.Description
agent_idstring

Calls: GET /v1/sdr/searches

Hunters 13

sdr_configure_hunterConfigure one hunternew

Turns one hunter on/off and/or sets the search fields it reads (only those listed in its inputs): competitor_pages (Competitor Engagement/Switching), target_cities (local business hunters, email discovery), decision_roles (Decision-maker matching, Website profile), linkedin_search_queries (LinkedIn hiring), industry_keywords / exclude_keywords / relevant_topics. Settings are search-wide.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringyesHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
enabledboolean
settingsobject
settings.competitor_pagesstring[]Competitor LinkedIn page URLs (Competitor Engagement and Competitor Switching hunters) max items 30
settings.target_citiesobject[]Optional cities inside target_regions (use sdr_find_cities). Empty = whole countries. max items 100
settings.target_cities[].countrystringyesISO-2 country code; must be one of target_regions min length 2; max length 2
settings.target_cities[].namestringyes
settings.target_cities[].admin1string
settings.target_cities[].admin1_codestring
settings.target_cities[].latnumber
settings.target_cities[].lngnumber
settings.decision_rolesstring[]Decision-maker job titles; used by Decision-maker matching and Website profile hunters max items 20
settings.linkedin_search_queriesobject[]max items 50
settings.linkedin_search_queries[].querystringyes
settings.linkedin_search_queries[].location_hintstring
settings.linkedin_search_queries[].rationalestring
settings.industry_keywordsstring[]
settings.exclude_keywordsstring[]
settings.relevant_topicsobject

Calls: PATCH /v1/sdr/searches/:id/hunters/:source

sdr_healthGet Signal Search health dashboard

Operator-level health view: states, signals/contacts produced, credits spent, recent events, learning insights for a single search.

ArgumentTypeReq.Description
search_idstringyes

Calls: GET /v1/sdr/searches/:id/health

sdr_hunter_dailySignals & contacts per day for one hunternew

Read-only. The hunter page chart: signals and contacts per day for one hunter (days 2–60, default 14).

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringyesHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
daysintegermin 2; max 60

Calls: GET /v1/sdr/searches/:id/hunters/:source/daily

sdr_hunter_detailHunter detail pagenew

Read-only. One hunter of a search: run state, signals in 30 days, signals & contacts per day (days 2–60, default 14), the contacts it found (incl. pending/phone-only), and its settings.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringyesHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
daysintegermin 2; max 60
contacts_limitintegermin 1; max 200

Calls: GET /v1/sdr/searches/:id/hunters/:source

sdr_list_huntersList all SDR hunters (sources)updated

Read-only. The 24 hunter sources a search can run, with UI name, what each looks at, opt-in/paid-engine/LinkedIn-budget flags, whether it can be run by hand, and which search fields it reads. Use to map a hunter the user names ("Competitor Switching") to its id (g2_reviews) before sdr_set_hunters / sdr_configure_hunter / sdr_run_now. Hunters are not billed per run; each usable contact costs 5 credits.

No arguments.

Calls: GET /v1/sdr/hunters

sdr_pause_hunterPause ONE hunterupdated

Pauses one hunter of a search; the others keep running. Reversible with sdr_resume_hunter.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringyesHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Calls: POST /v1/sdr/searches/:id/sources/pause

sdr_preview_run_nowPreview running hunters nowupdatedpreview

Dry run of the hunter "Run" button. Lists which hunters would run (one source, or every enabled runnable hunter when omitted), flags paid engines and LinkedIn-budget hunters. Nothing runs.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringHunter source id; cross_pool and people_pool run automatically and cannot be started by hand one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Calls: POST /v1/sdr/searches/:id/run-now

sdr_resume_hunterResume ONE hunterupdated

Resumes one paused hunter of a search.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringyesHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Calls: POST /v1/sdr/searches/:id/sources/resume

sdr_run_nowRun hunters nowupdatedexecute

Starts (or wakes, if already running) one hunter or every enabled runnable hunter of a search. Paid engines use Vendo's provider budget, LinkedIn hunters the LinkedIn budget; contacts found cost 5 credits each. Call after sdr_preview_run_now.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sourcestringHunter source id; cross_pool and people_pool run automatically and cannot be started by hand one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Calls: POST /v1/sdr/searches/:id/run-now

sdr_run_statePrimary run state of a searchnew

Read-only. Overall run state of a search (running / paused / idle) with the current action.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: GET /v1/sdr/searches/:id/run-state

sdr_run_statesGet per-source hunter run-states

Per-source map of run state (running / paused / idle) for a Signal Search.

ArgumentTypeReq.Description
search_idstringyes

Calls: GET /v1/sdr/searches/:id/run-states

sdr_search_huntersHunters of one SDR searchnew

Read-only. Every hunter for a search: enabled or not, live run state, signals in 30 days, 7-day sparkline, and the current values of the settings it reads. The search page's hunter stations.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: GET /v1/sdr/searches/:id/hunters

sdr_set_huntersSwitch hunters on/off for a searchnew

Enables/disables hunters (the wizard's Hunters step). Pass enable/disable lists, or enabled_sources as the full list. On an active search new hunters start within ~10 min (budget-throttled ones ~30); disabled ones stop at their next cycle. To stop one immediately without disabling it use sdr_pause_hunter.

ArgumentTypeReq.Description
search_idstring (uuid)yes
enablestring[]items: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
disablestring[]items: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
enabled_sourcesstring[]items: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings

Calls: PATCH /v1/sdr/searches/:id/hunters

People (contacts) & ICP QA 16

sdr_bulk_delete_peopleRemove several contacts (no restore)newexecute

Removes up to 500 contacts of one search. No restore. Only after sdr_preview_bulk_delete_people was approved.

ArgumentTypeReq.Description
search_idstring (uuid)yes
idsstring (uuid)[]yesmin items 1; max items 500

Calls: POST /v1/sdr/people/bulk-delete

sdr_delete_personRemove a contact (no restore)newexecute

Removes one contact from the search: hidden from lists, counts, exports and outreach. No restore, no refund. Only after sdr_preview_delete_person was approved.

ArgumentTypeReq.Description
idstring (uuid)yes
search_idstring (uuid)yes

Calls: DELETE /v1/sdr/people/:id

sdr_export_people_csvExport SDR contacts as CSV

Export the contacts of an SDR search as CSV text (name, title, email, LinkedIn, location, company). Contacts ONLY, deliberately carries no scores or signal reasoning, because this file leaves the platform. Returns the raw CSV; can be large. Read-only, free.

ArgumentTypeReq.Description
search_idstringyes
companystring
rolestring
min_relevancenumber

Calls: GET /v1/sdr/people/export.csv

sdr_export_personas_to_audienceExport SDR contacts to an audienceupdated

Creates a custom audience from a search's contacts with an email (segment fit by default, optional roles or explicit ids). No credits. The audience can then be used in any campaign.

ArgumentTypeReq.Description
search_idstring (uuid)yes
namestringyesName of the new audience min length 1; max length 200
fit_segmentstringone of: fit, fit_uncertain, all, uncertain, unfit, unchecked
only_with_emailboolean
rolesstring[]
idsstring (uuid)[]

Calls: POST /v1/sdr/people/export-to-audience

sdr_get_person_detailWhy was this person picked?updated

Read-only. Provenance of one contact: hunter, query, triggering signal, scores and the reasoning chain. Needs the search_id the contact belongs to.

ArgumentTypeReq.Description
idstring (uuid)yesContact id
search_idstring (uuid)yes

Calls: GET /v1/sdr/people/:id/detail

sdr_icp_qa_autocorrectKeyword auto-correction on/offnew

Lets the ICP check rewrite the search's industry keywords automatically (default off).

ArgumentTypeReq.Description
search_idstring (uuid)yes
enabledbooleanyes

Calls: PATCH /v1/sdr/searches/:id/icp-qa/autocorrect

sdr_icp_qa_historyICP check historynew

Read-only. Past ICP-QA runs (checked, fits, uncertain, unfit, quality %, keyword changes) and whether keyword auto-correction is on.

ArgumentTypeReq.Description
search_idstring (uuid)yes
limitintegermin 1; max 100

Calls: GET /v1/sdr/searches/:id/icp-qa/history

sdr_icp_qa_rollbackRoll keywords back to a QA runnew

Restores the search's industry_keywords to the snapshot taken before the given ICP-QA run (undo an auto-correction).

ArgumentTypeReq.Description
search_idstring (uuid)yes
run_idstring (uuid)yes

Calls: POST /v1/sdr/searches/:id/icp-qa/rollback

sdr_icp_qa_runICP check nownew

People page "Check now": classifies unchecked contacts of a search as Fit / Uncertain / Not a fit (AI, not billed) and may suggest keyword changes. Returns the run, or null when nothing is unchecked.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: POST /v1/sdr/searches/:id/icp-qa/run

sdr_list_personasList SDR contactsupdated

Read-only. Contacts of a search, newest first. Filters: group (fits/uncertain/unfit/unchecked), source (contacts of one hunter, incl. pending/phone-only), company, role, min_relevance; paging up to 200.

ArgumentTypeReq.Description
search_idstring (uuid)yes
groupstringone of: fits, uncertain, unfit, unchecked
sourcestringHunter source id (see sdr_list_hunters) one of: linkedin_jobs, linkedin_company, linkedin_activity, ai_web_hunter, social_signal_hunter, crunchbase_rss, hn_hiring, g2_reviews, tech_stack, connected_people, email_enricher, linkedin_engagement, linkedin_job_change, linkedin_competitor_followers, cross_pool, people_pool, github_issues, adzuna_jobs, corporate_registries, web_news_radar, web_pulse_hunter, google_places, web_profile, local_listings
companystring
rolestring
min_relevanceintegermin 0; max 100
limitintegermin 1; max 200
offsetintegermin 0

Calls: GET /v1/sdr/people

sdr_people_group_countsFit / Uncertain / Not a fit countsnew

Read-only. How many contacts are Fit, Uncertain, Not a fit and unchecked. only_with_email=false counts email-or-LinkedIn reachable contacts (what outreach "Who to contact" uses). Use before choosing icp_segment.

ArgumentTypeReq.Description
search_idstring (uuid)yes
only_with_emailboolean

Calls: GET /v1/sdr/people/group-counts

sdr_persona_statsSDR contact statsupdated

Read-only. Contact totals, by role, top companies, daily series (7, 14 or 28 days), credits spent.

ArgumentTypeReq.Description
search_idstring (uuid)yes
daysnumberone of: 7, 14, 28

Calls: GET /v1/sdr/people/stats

sdr_preview_bulk_delete_peoplePreview removing several contactsnewpreview

Dry run of multi-select delete: checks every id belongs to the search and counts them by group.

ArgumentTypeReq.Description
search_idstring (uuid)yes
idsstring (uuid)[]yesmin items 1; max items 500

Calls: POST /v1/sdr/people/bulk-delete

sdr_preview_delete_personPreview removing a contactnewpreview

Dry run. Shows the contact that would be removed. Nothing changes.

ArgumentTypeReq.Description
idstring (uuid)yes
search_idstring (uuid)yes

Calls: DELETE /v1/sdr/people/:id

sdr_preview_set_person_groupPreview moving a contact between groupsnewpreview

Dry run. Shows the move and whether it costs credits: moving a contact the ICP check left free to Fit charges 5 credits.

ArgumentTypeReq.Description
idstring (uuid)yes
search_idstring (uuid)yes
groupstringyesone of: fits, uncertain, unfit

Calls: PATCH /v1/sdr/people/:id/group

sdr_set_person_groupMove a contact to Fit / Uncertain / Not a fitnewexecute

Overrides the ICP verdict for one contact. Moving a free (precheck-skipped) contact to Fit charges 5 credits (402 if the balance is too low). Call sdr_preview_set_person_group first when moving to fits.

ArgumentTypeReq.Description
idstring (uuid)yes
search_idstring (uuid)yes
groupstringyesone of: fits, uncertain, unfit

Calls: PATCH /v1/sdr/people/:id/group

Outreach 13

sdr_delete_outreachDelete the SDR outreach campaign (irreversible)

Delete the SDR outreach campaign. The Signal Search and its hunters CONTINUE running. Only call after sdr_preview_delete_outreach.

ArgumentTypeReq.Description
search_idstringyes

Calls: DELETE /v1/sdr/searches/:id/outreach

sdr_get_outreachGet SDR outreach status for a search

Read the SDR outreach campaign, channels enabled, ready/queued/sent counts, current content config.

ArgumentTypeReq.Description
search_idstringyes

Calls: GET /v1/sdr/searches/:id/outreach

sdr_get_outreach_contentGet SDR outreach content (templates)

Read the configured email subject/body templates and LinkedIn invite template for SDR outreach.

ArgumentTypeReq.Description
search_idstringyes

Calls: GET /v1/sdr/searches/:id/outreach/content

sdr_launch_outreachLaunch SDR outreach (sends real messages)updatedexecute

Launches outreach: real emails and/or LinkedIn invites start in the working window and new contacts keep being picked up. Each newly queued contact costs 5 credits. Managed senders need a paid plan; client_owned needs a connected mailbox. Only after the user approved sdr_preview_launch_outreach.

ArgumentTypeReq.Description
search_idstring (uuid)yes
channelsstring[]items: email, linkedin
email_sender_modestringmanaged = Vendo sender pool (paid plans only); client_owned = the workspace's own connected mailboxes one of: managed, client_owned
client_email_providerstring | nullone of: google, microsoft
selected_linkedin_account_idsstring[]Ids from sdr_get_outreach → linkedin_accounts; empty = every connected account in status OK
timezonestringIANA time zone, e.g. Europe/London
work_start_hourintegermin 0; max 23
work_end_hourintegermin 0; max 23
exclude_weekendsboolean
daily_email_limitintegermin 1; max 500
daily_linkedin_limitintegermin 1; max 200
branch_idstring | nullCRM branch for replies: a branch id from crm_list_branches, or null/'general' for General
icp_segmentstringWho to contact: fit (Fit + unchecked, default), fit_uncertain, all; legacy: uncertain, unfit, unchecked one of: fit, fit_uncertain, all, uncertain, unfit, unchecked

Calls: POST /v1/sdr/searches/:id/outreach/launch

sdr_list_outreach_processesSDR processes on the Sales Process pagenew

Read-only. SDR outreach processes of the workspace as listed on the Sales Process page (status, channels, counters).

No arguments.

Calls: GET /v1/sdr/outreach/sales-process

sdr_pause_outreachPause SDR outreach delivery

Stop sending new messages on this SDR campaign. Reversible.

ArgumentTypeReq.Description
search_idstringyes

Calls: POST /v1/sdr/searches/:id/outreach/pause

sdr_preview_delete_outreachPreview deleting an SDR outreach campaignpreview

Show what would happen. The Signal Search ITSELF is NOT deleted, only the outreach campaign.

ArgumentTypeReq.Description
search_idstringyes

Calls: DELETE /v1/sdr/searches/:id/outreach

sdr_preview_launch_outreachPreview launching SDR outreachupdatedpreview

Dry run of "Save and Launch". Shows the effective settings (request + saved + defaults), contacts in the chosen segment, ready email/LinkedIn contacts, predicted blockers (no LinkedIn account, no own mailbox), the paid-plan rule for managed senders and the delivery window. Nothing is sent. Always call first and show it to the user.

ArgumentTypeReq.Description
search_idstring (uuid)yes
channelsstring[]items: email, linkedin
email_sender_modestringmanaged = Vendo sender pool (paid plans only); client_owned = the workspace's own connected mailboxes one of: managed, client_owned
client_email_providerstring | nullone of: google, microsoft
selected_linkedin_account_idsstring[]Ids from sdr_get_outreach → linkedin_accounts; empty = every connected account in status OK
timezonestringIANA time zone, e.g. Europe/London
work_start_hourintegermin 0; max 23
work_end_hourintegermin 0; max 23
exclude_weekendsboolean
daily_email_limitintegermin 1; max 500
daily_linkedin_limitintegermin 1; max 200
branch_idstring | nullCRM branch for replies: a branch id from crm_list_branches, or null/'general' for General
icp_segmentstringWho to contact: fit (Fit + unchecked, default), fit_uncertain, all; legacy: uncertain, unfit, unchecked one of: fit, fit_uncertain, all, uncertain, unfit, unchecked

Calls: POST /v1/sdr/searches/:id/outreach/launch

sdr_preview_outreach_contentPreview a personalized SDR messageexecute

Render an example personalized email/LinkedIn message for a specific contact. Costs ~1 Claude call.

ArgumentTypeReq.Description
search_idstringyes

Calls: POST /v1/sdr/searches/:id/outreach/content/preview

sdr_resume_outreachResume SDR outreach delivery

Resume a paused SDR outreach campaign.

ArgumentTypeReq.Description
search_idstringyes

Calls: POST /v1/sdr/searches/:id/outreach/resume

sdr_save_outreach_settingsSave SDR outreach settings (no launch)new

Outreach window 1 "Save": Who to contact (icp_segment), channels, email sender mode/provider, LinkedIn accounts, time zone, working hours, weekends, daily limits, CRM branch. Partial: missing fields keep their saved values. Never launches: a new process stays a draft; a running one keeps its status and applies the change at once (widening Who to contact on a running process queues more contacts at 5 credits each). Set dry_run=true to preview.

ArgumentTypeReq.Description
search_idstring (uuid)yes
channelsstring[]items: email, linkedin
email_sender_modestringmanaged = Vendo sender pool (paid plans only); client_owned = the workspace's own connected mailboxes one of: managed, client_owned
client_email_providerstring | nullone of: google, microsoft
selected_linkedin_account_idsstring[]Ids from sdr_get_outreach → linkedin_accounts; empty = every connected account in status OK
timezonestringIANA time zone, e.g. Europe/London
work_start_hourintegermin 0; max 23
work_end_hourintegermin 0; max 23
exclude_weekendsboolean
daily_email_limitintegermin 1; max 500
daily_linkedin_limitintegermin 1; max 200
branch_idstring | nullCRM branch for replies: a branch id from crm_list_branches, or null/'general' for General
icp_segmentstringWho to contact: fit (Fit + unchecked, default), fit_uncertain, all; legacy: uncertain, unfit, unchecked one of: fit, fit_uncertain, all, uncertain, unfit, unchecked
dry_runboolean

Calls: PATCH /v1/sdr/searches/:id/outreach/settings

sdr_update_outreach_contentEdit SDR outreach messagesupdated

Outreach windows 2–3: email (mode ai/fixed/html, prompt, tone, ≤5 fixed variants with {{placeholders}}, per-step follow-ups, own-HTML steps, signature, auto-reply) and LinkedIn (invite on/off, ai/exact texts, follow-up, delay after accept, conversation rules, auto-reply delays, signature, Calendly). Partial; takes effect on new sends. Follow-up changes are blocked while an email sequence is already sending.

ArgumentTypeReq.Description
search_idstring (uuid)yes
emailobject
email.modestringai = model writes each email from prompt; fixed = variants; html = html_steps one of: ai, fixed, html
email.promptstring
email.tonestringone of: formal, casual, friendly
email.variantsobject[]Fixed texts, rotated per recipient. Placeholders: {{first_name}} {{last_name}} {{full_name}} {{company_name}} {{position}} {{city}} {{country}} max items 5
email.variants[].subjectstringyes
email.variants[].bodystringyes
email.followupsobject[]Per-step follow-ups; delay counts from the previous email max items 7
email.followups[].delay_daysintegeryesmin 1; max 90
email.followups[].promptstring
email.followups[].subjectstring
email.followups[].bodystring
email.html_stepsobject[]max items 20
email.html_steps[].subjectstringyes
email.html_steps[].htmlstringyes
email.html_steps[].delay_daysinteger
email.signature_enabledboolean
email.signature_textstring
email.followups_enabledboolean
email.max_followupsintegermin 0; max 7
email.followup_interval_daysintegermin 1; max 90
email.auto_reply_enabledboolean
email.auto_reply_delay_minutesintegermin 1; max 120
email.validation_note_enabledboolean
linkedinobject
linkedin.invite_enabledboolean
linkedin.invite_modestringone of: ai, exact
linkedin.invite_tonestringone of: formal, casual, friendly
linkedin.invite_promptstring
linkedin.invite_exact_textstring
linkedin.followup_enabledboolean
linkedin.followup_modestringone of: ai, exact
linkedin.followup_promptstring
linkedin.followup_exact_textstring
linkedin.followup_ctastring
linkedin.followup_delay_minutesintegerMinutes between accepted invite and first message min 0; max 20160
linkedin.calendly_urlstring
linkedin.conversation_rulesstringWhat we want from these conversations: goal, CTA, links. Applied to AI invites, follow-ups and smart replies. max length 3000
linkedin.auto_reply_enabledboolean
linkedin.reply_delay_min_minutesintegermin 1; max 60
linkedin.reply_delay_max_minutesintegermin 1; max 120
linkedin.signature_enabledboolean
linkedin.signature_textstring
linkedin.signature_modestringone of: first_message, every_message

Calls: PATCH /v1/sdr/searches/:id/outreach/content

Search analytics 10

sdr_activity_feedLive activity feed for an SDR search

Recent discovery + outreach events for one SDR search, newest first, the same ticker the dashboard shows. Read-only, free.

ArgumentTypeReq.Description
search_idstringyes
limitnumberDefault 40

Calls: GET /v1/sdr/searches/:id/outreach/activity-feed

sdr_company_detailHot company popupnew

Read-only. One company of a search: company card (website, size, location, links), decision-makers found with fit, other known people at the company and its live signals. part=card returns only the card and people pool (slower source, up to 20 s).

ArgumentTypeReq.Description
search_idstring (uuid)yes
company_idstring (uuid)yescompany_id from sdr_company_heat
partstringone of: full, card
namestring
domainstring

Calls: GET /v1/sdr/searches/:id/companies/:companyId

sdr_daily_by_hunterSignals per day per hunternew

Read-only. Station sparklines: signals per day for every hunter of a search (days 2–60, default 14).

ArgumentTypeReq.Description
search_idstring (uuid)yes
daysintegermin 2; max 60

Calls: GET /v1/sdr/searches/:id/stats/daily

sdr_eventsLive hunter events (polling)new

Read-only. What the hunters are doing right now (queries, companies resolved, signals saved, contacts found). Pass since = next_since of the previous call to get only new events. Best effort: last 150 events per search, lost on restart.

ArgumentTypeReq.Description
search_idstring (uuid)yes
sincestringISO timestamp
limitintegermin 1; max 200

Calls: GET /v1/sdr/searches/:id/events

sdr_export_companies_csvExport companies CSVnew

Read-only. CSV of every company the search found: Company, Domain, Heat, ICP fit, Signal count, Signal types, Contact status, Contacts found.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: GET /v1/sdr/searches/:id/companies/export.csv

sdr_icp_reviewICP Review panelnew

Read-only. Uncertain contacts waiting for re-check, last run results, totals and next scheduled run.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: GET /v1/sdr/searches/:id/icp-review

sdr_icp_review_runRe-check Uncertain contacts nownew

ICP Review "Re-check now": re-judges this search's Uncertain contacts (bounded per run, one run at a time). Contacts moved to Fit become billable (5 credits) when used.

ArgumentTypeReq.Description
search_idstring (uuid)yes

Calls: POST /v1/sdr/searches/:id/icp-review/run

sdr_learning_recomputeRecompute signal-learning weights for a search

Recalculate per-signal-type weights from outreach outcomes (replies/meetings vs sends) for one search. Safe, no cost; use when the user asks to refresh/recalculate Signal Learning.

ArgumentTypeReq.Description
search_idstringyes

Calls: POST /v1/sdr/searches/:id/learning-recompute

sdr_outreach_summarySent/queued counters for an SDR search

Per-channel sent and queued counts for one SDR search. Read-only, free.

ArgumentTypeReq.Description
search_idstringyes

Calls: GET /v1/sdr/searches/:id/outreach/summary

sdr_signal_statsSignal stats by hunterupdated

Read-only. KPI cards: fresh signals, signals and contacts by source over the last N days (1–90, default 30).

ArgumentTypeReq.Description
search_idstring (uuid)yes
daysintegermin 1; max 90

Calls: GET /v1/sdr/searches/:id/stats

Campaigns tools 77

Lifecycle 15

archive_campaignArchive a campaignupdatedexecute

Archive the campaign: status becomes completed, all its outreach stops and it is hidden from the default lists (history stays readable with include_archived). Not reversible. Only call after preview_archive_campaign and the user's explicit approval.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/archive

create_campaignCreate a campaign (DRAFT, not launched)updated

Create a classic campaign in DRAFT, nobody is contacted until launch_campaign. Needs an audience (audienceId from save_audience/list_audiences, OR customAudienceId of a READY uploaded/CRM list) and should get an agentId (a campaign cannot be launched without an agent: AGENT_REQUIRED). branchId = CRM branch for replies (crm_list_branches; null = General). Settings (all optional): channels, dailyLimit, dailyEmailLimit, dailyLinkedinLimit, timezone, workingHours, excludeWeekends, campaignDurationDays, customInstructions (Communication Rules incl. form of address), emailSignature, notifyEmails, ccEmail, autoResponseEnabled, autoResponseDelayMinutes, followUpInterval, maxFollowUps, trackOpens, emailSenderMode (managed needs a paid plan), senderEmailIds, emailContent {mode, prompt, tone, variants, htmlSteps, followUps[{delayDays, prompt|subject+body}]}, linkedinContent {mode, invite_prompt, invite_tone, invite_exact_text, followup_delay_minutes, followup_exact_text}, selectedLinkedinAccountIds, linkedinAutoReplyDisabled. Ids are checked against the workspace. Free, no sending.

ArgumentTypeReq.Description
namestringyes
audienceIdstringCampaign audience id
customAudienceIdstringInstead of audienceId: id of a READY custom audience (uploaded CSV/CRM list); an audience is created from it
agentIdstringSales agent id (list_agents). Required before launch.
branchIdstring | nullCRM branch id for replies; null = General
descriptionstring
settingsobject
settings.channelsstring[]Channels the campaign uses (default ['email']). items: email, linkedin, phone_call
settings.dailyLimitintegerNew contacts emailed per day. Must not exceed sender capacity (list_sender_emails / list_client_mailboxes). Default 200. min 0
settings.dailyEmailLimitintegerEmails per day; the app keeps it equal to dailyLimit. min 0
settings.dailyLinkedinLimitintegerCampaign-wide LinkedIn invites per day across ALL its accounts (hard limit). Each account also has its own warm-up cap. min 0
settings.timezonestringIANA timezone of the sending window, e.g. Europe/London (default).
settings.workingHoursarray[startHour, endHour] in the campaign timezone (default [9,18]). min items 2; max items 2
settings.excludeWeekendsbooleanDo not send on Saturday/Sunday (default true).
settings.campaignDurationDaysintegerPlanned duration in days (default 30). min 1
settings.customInstructionsstringCommunication Rules: CTA, links, promises, what never to say. Overrides prompt defaults in emails, follow-ups, AI auto-replies and LinkedIn. Also sets the informal/formal form of address (e.g. ти/Ви, du/Sie) when the rules say so. max length 6000
settings.emailSignaturestringHTML signature appended to emails ('' = no signature).
settings.notifyEmailsstring[]Addresses notified when a lead replies.
settings.ccEmailstringAddress put in CC of outgoing emails.
settings.autoResponseEnabledbooleanAI answers email replies automatically (default true).
settings.autoResponseDelayMinutesintegerDelay before the AI email auto-reply (default 10). min 0
settings.followUpIntervalintegerLegacy: days between follow-ups, used only when emailContent.followUps is empty (default 3). min 0
settings.maxFollowUpsintegerLegacy: number of follow-ups when emailContent.followUps is empty (default 3). min 0; max 7
settings.trackOpensbooleanInsert the open-tracking pixel.
settings.trackLinksbooleanSaved like the app (currently not read by the sender).
settings.stopOnNegativebooleanSaved like the app (currently not read by the sender).
settings.speedTierstringone of: conservative, balanced, aggressive
settings.emailSenderModestringmanaged = Vendo warmed pool (PAID PLAN REQUIRED to launch); client_owned = the workspace's own mailboxes (connect_client_mailbox). Default managed. one of: managed, client_owned
settings.clientEmailProviderstring | nullInformational provider of own mailboxes. one of: google, microsoft
settings.senderEmailIdsstring[]Restrict sending to these sender ids from list_sender_emails (empty = auto-assign at launch).
settings.emailContentobjectEmail content (wizard step 4). On a running campaign a change to steps/delays is NOT applied once anything was sent; see settings.smartleadSequencePending in get_campaign.
settings.emailContent.modestringyesai = AI writes each email from the prompt; fixed = your text with {{first_name}}-style placeholders; html = your own HTML steps. one of: ai, fixed, html
settings.emailContent.promptstringAI writing rules for the first email.
settings.emailContent.tonestringone of: formal, casual, friendly
settings.emailContent.variantsobject[]Fixed mode: up to 5 A/B variants of the first email. max items 5
settings.emailContent.htmlStepsobject[]HTML mode: one entry per email; delayDays = days after the previous email (not on the first). max items 20
settings.emailContent.followUpsobject[]Follow-ups after the first email (max 7). max items 7
settings.linkedinContentobjectLinkedIn content (wizard step 5).
settings.linkedinContent.modestringyesone of: ai, fixed
settings.linkedinContent.invite_promptstringAI rules for the connection note (also used for the form of address).
settings.linkedinContent.invite_tonestringone of: formal, casual, friendly
settings.linkedinContent.invite_exact_textstringFixed connection note (LinkedIn sends max 200 chars). max length 300
settings.linkedinContent.followup_delay_minutesintegerDelay of the first message after the invite is accepted. min 0; max 43200
settings.linkedinContent.followup_exact_textstringFixed first message after connect. max length 1500
settings.selectedLinkedinAccountIdsstring[]LinkedIn accounts used by this campaign (ids from list_linkedin_accounts). Empty = all connected accounts at launch.
settings.linkedinAutoReplyDisabledbooleanSwitch LinkedIn AI auto-replies OFF for this campaign only (per-account settings: update_linkedin_account_settings).

Calls: POST /v1/campaigns

delete_campaignDelete a campaign (irreversible)

Permanently delete a campaign. NOT reversible. Only call after the user has reviewed preview_delete_campaign output and explicitly approved.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/campaigns/:id

duplicate_campaignDuplicate a campaignnew

Copy a campaign (settings + audience) into a new DRAFT named '<name> (Copy)'. Nothing is sent. Note: the copy re-uses company-based audiences only and has no CRM branch; check get_campaign of the copy before launching.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/duplicate

get_campaignGet a campaignupdated

Full campaign: status, settings (all wizard fields), sequence, channel paused flags, channelStatuses, today's sends, stats.totalContacts. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id

launch_campaignLaunch / resume a campaign (sends real outreach)updatedexecute

Start (or resume a paused) campaign exactly like the app: starts email sending and queues LinkedIn on the selected (or all connected) accounts. Contacts receive REAL outreach in the working hours; credits are spent as messages go out. Only call AFTER the user reviewed preview_launch_campaign and approved it. Returns the campaign plus linkedin_activation.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
linkedin_account_idsstring[]

Calls: POST /v1/campaigns/:id/launch

list_campaignsList campaignsupdated

List classic Sales Process campaigns with status and live stats. Archived campaigns are hidden unless include_archived=true. Read-only.

ArgumentTypeReq.Description
include_archivedbooleanAlso return archived campaigns

Calls: GET /v1/campaigns

pause_campaignPause a campaign (all channels)

Pause a running campaign across every channel. Reversible, call launch_campaign to resume.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/campaigns/:id/pause

pause_channelPause one channel of a campaign

Pause a single channel (email, linkedin, phone_call, voice_call) on a running campaign. Other channels keep going.

ArgumentTypeReq.Description
idstringyes
channelstringyesone of: email, linkedin, phone_call, voice_call

Calls: POST /v1/campaigns/:id/pause-channel

preview_archive_campaignPreview archiving a campaignnewpreview

Show what archiving would do, NOTHING CHANGES: current status and channels, what stops (an active campaign stops ALL outreach at once and releases its mailboxes / LinkedIn accounts), what is kept (history, threads, stats, CRM leads). Archiving cannot be undone (duplicate_campaign to run the same setup again). ALWAYS call first and show the user before archive_campaign.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/archive

preview_delete_campaignPreview deleting a campaignpreview

Show what would happen if you delete a campaign. The campaign is NOT deleted.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/campaigns/:id

preview_launch_campaignPreview launching a campaignupdatedpreview

Readiness report for launch/resume, NOTHING IS SENT: ready flag, blockers (AGENT_REQUIRED, AUDIENCE_REQUIRED, AUDIENCE_EMPTY (0 contacts), CLIENT_MAILBOX_NOT_CONNECTED, PAID_PLAN_REQUIRED for Vendo managed senders, INVALID_STATUS), warnings, real recipient count, channels, daily email/LinkedIn limits, sender mode, email steps, schedule and the LinkedIn accounts that will be activated. ALWAYS call this first and show the result to the user before launch_campaign.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
linkedin_account_idsstring[]Override which LinkedIn accounts get activated (default: settings.selectedLinkedinAccountIds, else all connected)

Calls: POST /v1/campaigns/:id/launch

resume_channelResume one channel of a campaign

Resume a paused channel on a campaign.

ArgumentTypeReq.Description
idstringyes
channelstringyesone of: email, linkedin, phone_call, voice_call

Calls: POST /v1/campaigns/:id/resume-channel

sync_campaign_statsRefresh campaign email countersnew

Pull the latest sent/opened/clicked/replied/bounced counters from the sending provider and store them. Max once per minute per campaign. No credits.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/sync-stats

update_campaignUpdate campaign (wizard settings)updated

Change a campaign's name, description or any wizard setting (partial: send only what changes; settings are merged). agentId and branchId can be changed only while the campaign is a draft. Settings (all optional): channels, dailyLimit, dailyEmailLimit, dailyLinkedinLimit, timezone, workingHours, excludeWeekends, campaignDurationDays, customInstructions (Communication Rules incl. form of address), emailSignature, notifyEmails, ccEmail, autoResponseEnabled, autoResponseDelayMinutes, followUpInterval, maxFollowUps, trackOpens, emailSenderMode (managed needs a paid plan), senderEmailIds, emailContent {mode, prompt, tone, variants, htmlSteps, followUps[{delayDays, prompt|subject+body}]}, linkedinContent {mode, invite_prompt, invite_tone, invite_exact_text, followup_delay_minutes, followup_exact_text}, selectedLinkedinAccountIds, linkedinAutoReplyDisabled. On a running campaign, changed email steps/delays are not applied once anything was sent (settings.smartleadSequencePending). No sending, no credits.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
namestring
descriptionstring
agentIdstringDraft only
branchIdstring | nullDraft only; null = General
settingsobject
settings.channelsstring[]Channels the campaign uses (default ['email']). items: email, linkedin, phone_call
settings.dailyLimitintegerNew contacts emailed per day. Must not exceed sender capacity (list_sender_emails / list_client_mailboxes). Default 200. min 0
settings.dailyEmailLimitintegerEmails per day; the app keeps it equal to dailyLimit. min 0
settings.dailyLinkedinLimitintegerCampaign-wide LinkedIn invites per day across ALL its accounts (hard limit). Each account also has its own warm-up cap. min 0
settings.timezonestringIANA timezone of the sending window, e.g. Europe/London (default).
settings.workingHoursarray[startHour, endHour] in the campaign timezone (default [9,18]). min items 2; max items 2
settings.excludeWeekendsbooleanDo not send on Saturday/Sunday (default true).
settings.campaignDurationDaysintegerPlanned duration in days (default 30). min 1
settings.customInstructionsstringCommunication Rules: CTA, links, promises, what never to say. Overrides prompt defaults in emails, follow-ups, AI auto-replies and LinkedIn. Also sets the informal/formal form of address (e.g. ти/Ви, du/Sie) when the rules say so. max length 6000
settings.emailSignaturestringHTML signature appended to emails ('' = no signature).
settings.notifyEmailsstring[]Addresses notified when a lead replies.
settings.ccEmailstringAddress put in CC of outgoing emails.
settings.autoResponseEnabledbooleanAI answers email replies automatically (default true).
settings.autoResponseDelayMinutesintegerDelay before the AI email auto-reply (default 10). min 0
settings.followUpIntervalintegerLegacy: days between follow-ups, used only when emailContent.followUps is empty (default 3). min 0
settings.maxFollowUpsintegerLegacy: number of follow-ups when emailContent.followUps is empty (default 3). min 0; max 7
settings.trackOpensbooleanInsert the open-tracking pixel.
settings.trackLinksbooleanSaved like the app (currently not read by the sender).
settings.stopOnNegativebooleanSaved like the app (currently not read by the sender).
settings.speedTierstringone of: conservative, balanced, aggressive
settings.emailSenderModestringmanaged = Vendo warmed pool (PAID PLAN REQUIRED to launch); client_owned = the workspace's own mailboxes (connect_client_mailbox). Default managed. one of: managed, client_owned
settings.clientEmailProviderstring | nullInformational provider of own mailboxes. one of: google, microsoft
settings.senderEmailIdsstring[]Restrict sending to these sender ids from list_sender_emails (empty = auto-assign at launch).
settings.emailContentobjectEmail content (wizard step 4). On a running campaign a change to steps/delays is NOT applied once anything was sent; see settings.smartleadSequencePending in get_campaign.
settings.emailContent.modestringyesai = AI writes each email from the prompt; fixed = your text with {{first_name}}-style placeholders; html = your own HTML steps. one of: ai, fixed, html
settings.emailContent.promptstringAI writing rules for the first email.
settings.emailContent.tonestringone of: formal, casual, friendly
settings.emailContent.variantsobject[]Fixed mode: up to 5 A/B variants of the first email. max items 5
settings.emailContent.htmlStepsobject[]HTML mode: one entry per email; delayDays = days after the previous email (not on the first). max items 20
settings.emailContent.followUpsobject[]Follow-ups after the first email (max 7). max items 7
settings.linkedinContentobjectLinkedIn content (wizard step 5).
settings.linkedinContent.modestringyesone of: ai, fixed
settings.linkedinContent.invite_promptstringAI rules for the connection note (also used for the form of address).
settings.linkedinContent.invite_tonestringone of: formal, casual, friendly
settings.linkedinContent.invite_exact_textstringFixed connection note (LinkedIn sends max 200 chars). max length 300
settings.linkedinContent.followup_delay_minutesintegerDelay of the first message after the invite is accepted. min 0; max 43200
settings.linkedinContent.followup_exact_textstringFixed first message after connect. max length 1500
settings.selectedLinkedinAccountIdsstring[]LinkedIn accounts used by this campaign (ids from list_linkedin_accounts). Empty = all connected accounts at launch.
settings.linkedinAutoReplyDisabledbooleanSwitch LinkedIn AI auto-replies OFF for this campaign only (per-account settings: update_linkedin_account_settings).

Calls: PATCH /v1/campaigns/:id

Wizard previews 3

preview_campaign_emailPreview a campaign email (first or follow-up #k)newexecute

Render the exact email a campaign would send, first email or follow-up #k, with the writing rules, tone, Communication Rules or fixed text. Pass campaign_id to use its saved settings (any field you pass overrides them), or agent_id + rules for an unsaved draft. No sending, no credits.

ArgumentTypeReq.Description
campaign_idstring
agent_idstringRequired for AI mode when no campaign_id
modestringone of: ai, fixed
promptstringAI writing rules (for the follow-up when follow_up is set)
tonestringone of: formal, casual, friendly
custom_instructionsstringCommunication Rules
variantobjectFixed mode text
variant.subjectstringyes
variant.bodystringyes
follow_upobjectPreview follow-up #index instead of the first email
follow_up.indexintegeryesmin 1; max 7
follow_up.delay_daysintegermin 1; max 90
follow_up.previousobject
follow_up.previous.subjectstring
follow_up.previous.bodystring
sampleobjectSample recipient used for the preview (defaults to a demo contact).
sample.contact_namestring
sample.company_namestring
sample.positionstring
sample.citystring
sample.countrystring
sample.websitestring

Calls: POST /v1/campaigns/preview/email

preview_campaign_linkedin_followupPreview the LinkedIn message after connectnewexecute

Render the first LinkedIn message sent after an invite is accepted (AI from the agent, or the fixed text with placeholders filled). Pass campaign_id to use saved settings. No sending, no credits.

ArgumentTypeReq.Description
campaign_idstring
agent_idstring
modestringone of: ai, fixed
exact_textstringmax length 1500
sampleobjectSample recipient used for the preview (defaults to a demo contact).
sample.contact_namestring
sample.company_namestring
sample.positionstring
sample.citystring
sample.countrystring
sample.websitestring

Calls: POST /v1/campaigns/preview/linkedin-followup

preview_campaign_linkedin_invitePreview a campaign LinkedIn invitenewexecute

Render the LinkedIn connection note a campaign would send (AI from rules/tone, or fixed text with placeholders), with its length vs the 200-char limit. Pass campaign_id to use saved settings. No sending, no credits.

ArgumentTypeReq.Description
campaign_idstring
agent_idstring
modestringone of: ai, fixed
promptstring
tonestringone of: formal, casual, friendly
exact_textstringmax length 300
sampleobjectSample recipient used for the preview (defaults to a demo contact).
sample.contact_namestring
sample.company_namestring
sample.positionstring
sample.citystring
sample.countrystring
sample.websitestring

Calls: POST /v1/campaigns/preview/linkedin-invite

Emails, replies & delivery 13

get_campaign_emailOpen one campaign emailnew

One sent email by email id (from list_campaign_emails / list_campaign_replies): content, recipient, related open/click/reply events, the reply and the conversation. Read-only.

ArgumentTypeReq.Description
email_idstringyes

Calls: GET /v1/campaigns/emails/:emailId

list_campaign_email_leadsEngaged email leadsnew

Recipients with their open/click/reply counts, read live from the sending provider (throttled: once per 15 s per campaign). Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
filterstringone of: opened, clicked, replied, all

Calls: GET /v1/campaigns/:id/email-leads

list_campaign_emailsList sent / queued / prepared emailsnew

state=sent: emails already sent (filter by status, search by address); state=queued: scheduled/queued emails waiting to go out; state=prepared: pre-generated emails pipeline with status counts and preparation progress. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
statestringyesone of: sent, queued, prepared
statusstringsent: sent|delivered|opened|clicked|replied|bounced; prepared: pipeline status e.g. ready_to_send, scheduled
searchstringSearch by email address (sent/queued)
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/emails/sent, GET /v1/campaigns/:id/emails/queued, GET /v1/campaigns/:id/emails/prepared

list_campaign_repliesCampaign replies inboxnew

Email replies received by a campaign with body and classification: real (a person answered), auto_reply (out of office), bounce. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
typestringone of: real, auto_reply, bounce

Calls: GET /v1/campaigns/:id/replies

prepare_campaign_emailsPre-generate campaign emails (legacy)new

Generate emails for every contact ahead of sending (legacy company-based audiences only; people/custom-audience campaigns prepare automatically after launch). Can take minutes. Nothing is sent. No credits.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/emails/prepare

preview_reply_to_campaign_emailPreview a manual reply to a leadnewpreview

Dry-run of replying to a lead's email reply: shows recipient, subject and the exact text. Nothing is sent. Call before reply_to_campaign_email and show the text to the user.

ArgumentTypeReq.Description
email_idstringyes
bodystringyesReply body (HTML allowed)

Calls: POST /v1/campaigns/emails/:emailId/reply

preview_schedule_campaign_deliveryPreview scheduling prepared emailsnewpreview

Dry-run: how many ready prepared emails would be scheduled into the campaign working hours. Nothing changes. Call before schedule_campaign_delivery.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/emails/schedule-delivery

preview_send_campaign_emails_nowPreview sending prepared emails nownewpreview

Dry-run: how many prepared emails would be sent immediately (outside the schedule). Nothing is sent.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
limitintegerMax emails (default 10) min 1; max 100

Calls: POST /v1/campaigns/:id/emails/send-now

refresh_campaign_email_replyReload a reply from the providernew

Fetch the latest reply text for an email from the sending provider (when a reply shows as empty). No credits, nothing sent.

ArgumentTypeReq.Description
email_idstringyes

Calls: POST /v1/campaigns/emails/:emailId/refresh-reply

reply_to_campaign_emailSend a manual reply to a lead (REAL email)newexecute

Send the reply immediately in the lead's thread (bypasses daily limits). REAL email. Only after the user approved the exact text from preview_reply_to_campaign_email.

ArgumentTypeReq.Description
email_idstringyes
bodystringyes

Calls: POST /v1/campaigns/emails/:emailId/reply

retry_campaign_email_preparationRetry failed email preparationnew

Put prepared emails that failed validation/generation back in the queue (max 3 attempts each). Nothing is sent.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/emails/retry-preparation

schedule_campaign_deliverySchedule prepared emails for deliverynewexecute

Spread all ready prepared emails over the campaign's working-hour slots; they are then SENT for real automatically. Only after the user approved preview_schedule_campaign_delivery.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: POST /v1/campaigns/:id/emails/schedule-delivery

send_campaign_emails_nowSend prepared emails now (REAL emails)newexecute

Send up to limit prepared emails immediately, ignoring the schedule. REAL emails to real people. Only after the user approved preview_send_campaign_emails_now.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
limitintegermin 1; max 100

Calls: POST /v1/campaigns/:id/emails/send-now

AI conversations (threads) 6

close_campaign_threadClose an AI email conversationnew

Mark the conversation closed (no more AI replies). Nothing is sent.

ArgumentTypeReq.Description
thread_idstringyes

Calls: POST /v1/campaigns/threads/:threadId/close

get_campaign_threadOpen an AI email conversationnew

One conversation with all messages (AI, human and contact). Read-only.

ArgumentTypeReq.Description
thread_idstringyes

Calls: GET /v1/campaigns/threads/:threadId

list_campaign_threadsAI email conversations of a campaignnew

Conversations the AI is handling for a campaign (filter by status ai_handling|human_takeover|closed and sentiment positive|objection|referral|unsubscribe|neutral), plus counters by status and sentiment. Only threads of the API key's user. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
statusstringone of: ai_handling, human_takeover, closed
sentimentstringone of: positive, objection, referral, unsubscribe, neutral
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/threads, GET /v1/campaigns/:id/threads/stats

preview_reply_in_campaign_threadPreview a human reply in a conversationnewpreview

Dry-run: recipient, subject (defaults to Re: last subject) and the exact text. Nothing is sent. Call before reply_in_campaign_thread and show the text to the user.

ArgumentTypeReq.Description
thread_idstringyes
bodystringyesPlain text
subjectstring
body_htmlstring

Calls: POST /v1/campaigns/threads/:threadId/reply

reply_in_campaign_threadSend a human reply in a conversation (REAL email)newexecute

Send the email to the contact now. REAL email. Only after the user approved preview_reply_in_campaign_thread. Consider takeover_campaign_thread first so the AI stops answering.

ArgumentTypeReq.Description
thread_idstringyes
bodystringyes
subjectstring
body_htmlstring

Calls: POST /v1/campaigns/threads/:threadId/reply

takeover_campaign_threadTake over an AI email conversationnew

Stop the AI from replying in this conversation; a human continues (reply_in_campaign_thread). Nothing is sent.

ArgumentTypeReq.Description
thread_idstringyes

Calls: POST /v1/campaigns/threads/:threadId/takeover

LinkedIn accounts, conversations & campaigns 17

activate_campaign_linkedinActivate LinkedIn on a campaignnewexecute

Queue the campaign audience for LinkedIn on the given (or selected/all connected) accounts and add linkedin to the channels. On an active campaign REAL connection invites start within the next cycles. Only after the user approved preview_activate_campaign_linkedin.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
account_idsstring[]

Calls: POST /v1/campaigns/:id/linkedin/activate

add_campaign_linkedin_accountAdd a LinkedIn account to a campaignnew

Add a connected account to a running campaign; pending contacts are rebalanced across accounts and backfilled. It starts sending invites in the next cycles. No credits.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
account_idstringyes

Calls: POST /v1/campaigns/:id/linkedin/accounts

connect_linkedin_accountGet a link to connect a LinkedIn account

Returns a hosted URL that a HUMAN must open in a browser to authorise a LinkedIn account. This tool cannot complete the connection itself, give the link to the user. No account exists until they finish the flow.

ArgumentTypeReq.Description
campaign_idstringOptionally bind the new account to a campaign

Calls: POST /v1/linkedin/accounts/connect

disconnect_linkedin_accountDisconnect a LinkedIn account

Remove a LinkedIn account from the workspace. Any campaign or SDR search sending from it stops immediately and daily capacity drops. Only call after the user reviewed preview_disconnect_linkedin_account and explicitly approved.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/linkedin/accounts/:id

get_campaign_linkedin_statsLinkedIn stats of a campaignnew

LinkedIn counters (total, pending, invites sent, connected, messaged, replied, failed), invites sent today, effective daily limit and active accounts; optionally for one account. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
account_idstring

Calls: GET /v1/campaigns/:id/linkedin/stats

get_linkedin_account_settingsLinkedIn account settingsnew

Per-account LinkedIn settings shared by every campaign and SDR process using the account: AI auto-replies (on/off, tone, instructions, pitch, booking link, reply delays, daily limit, working hours, agent, contact info), invites (on/off, tone, pitch, instructions, CTA), follow-ups (on/off, pitch, instructions, CTA) and signature (text, first_message|every_message). The first read creates defaults (pre-filled from campaign_id's agent).

ArgumentTypeReq.Description
account_idstringyes
campaign_idstringPre-fill defaults from this campaign's agent

Calls: GET /v1/linkedin/accounts/:accountId/settings

list_campaign_linkedin_accountsLinkedIn accounts working a campaignnew

Accounts working this campaign with per-account counters (pending, invites sent, connected, messaged, replied). Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/linkedin/accounts

list_campaign_linkedin_outreachLinkedIn outreach of a campaignnew

Contacts in the campaign's LinkedIn queue with their status (pending, invite_sent, connected, message_sent, replied, failed, skipped), optionally for one account. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
statusstringone of: pending, invite_sent, connected, message_sent, replied, failed, skipped
account_idstring
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/linkedin/outreach

list_linkedin_accountsList connected LinkedIn accountsupdated

LinkedIn accounts of the workspace and their status (ok = can send; ~70–80 invites/day each). Returns { role, accounts } (role is filled only with detailed=true). detailed=true adds owner/role, campaigns and SDR searches using each account, 7-day activity, prospecting caps, warm-up stage, premium tier and proxy country. Read-only.

ArgumentTypeReq.Description
detailedboolean

Calls: GET /v1/linkedin/accounts

preview_activate_campaign_linkedinPreview activating LinkedIn on a campaignnewpreview

Dry-run: which accounts would get the campaign audience queued, how many contacts and what is already queued. Nothing changes. Call before activate_campaign_linkedin.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
account_idsstring[]Default: settings.selectedLinkedinAccountIds, else all connected accounts

Calls: POST /v1/campaigns/:id/linkedin/activate

preview_disconnect_linkedin_accountPreview disconnecting a LinkedIn account (dry-run)preview

Dry-run of removing a LinkedIn account, showing the capacity that would be lost. ALWAYS call this first.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/linkedin/accounts/:id

preview_linkedin_account_messagesSample LinkedIn messages for an accountnewexecute

Sample invite, follow-up and auto-reply plus suggested pitch/signature for an account in a campaign (built from the campaign's agent). No sending, no credits.

ArgumentTypeReq.Description
account_idstringyes
campaign_idstringyes

Calls: GET /v1/linkedin/accounts/:accountId/preview-messages

preview_reconnect_linkedin_accountPreview reconnecting a LinkedIn account (dry-run)preview

Dry-run of re-authenticating a LinkedIn account. Explains the side-effects without doing anything. ALWAYS call this first, reconnecting changes the account id upstream and can orphan existing outreach history.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/linkedin/accounts/:id/reconnect

reconnect_linkedin_accountReconnect a LinkedIn account

Re-authenticate a LinkedIn account whose session died. Creates a NEW account id upstream: past outreach stays attached to the old id, so per-account history can look empty afterwards and running campaigns may need re-pointing. Only call after the user reviewed preview_reconnect_linkedin_account and approved.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/linkedin/accounts/:id/reconnect

remove_campaign_linkedin_accountRemove a LinkedIn account from a campaignnew

Stop using an account for this campaign; its pending contacts move to the remaining accounts. Already-sent invites stay.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
account_idstringyes

Calls: DELETE /v1/campaigns/:id/linkedin/accounts/:accountId

takeover_linkedin_contactTake over a LinkedIn contactnew

Stop the AI from messaging this LinkedIn contact (outreach id from list_campaign_linkedin_outreach or list_linkedin_conversations); pending auto-replies are cancelled. Nothing is sent.

ArgumentTypeReq.Description
outreach_idstringyes

Calls: POST /v1/linkedin/outreach/:outreachId/takeover

update_linkedin_account_settingsUpdate LinkedIn account settingsnew

Change AI auto-replies, invite / follow-up pitch, CTA, signature, reply delays/limits for ONE LinkedIn account (applies to all campaigns and SDR processes using it). Send only changed fields. To switch AI replies off for a single campaign use update_campaign settings.linkedinAutoReplyDisabled. No credits.

ArgumentTypeReq.Description
account_idstringyes
enabledbooleanAI auto-replies on/off
tonestringone of: formal, casual, friendly
custom_instructionsstring | null
product_pitchstring | null
calendly_urlstring | nullBooking link offered in replies
reply_delay_min_minutesintegermin 0; max 1440
reply_delay_max_minutesintegermin 0; max 1440
daily_reply_limitintegermin 0; max 500
working_hoursobject
working_hours.startintegeryesmin 0; max 23
working_hours.endintegeryesmin 1; max 24
timezonestring
agent_idstring | null
contact_infostring | null
invites_enabledboolean
invite_tonestringone of: formal, casual, friendly
invite_pitchstring | null
invite_custom_instructionsstring | null
invite_ctastring | null
followup_enabledboolean
followup_pitchstring | null
followup_custom_instructionsstring | null
followup_ctastring | null
signature_enabledboolean
signature_textstring | null
signature_modestringone of: first_message, every_message

Calls: PATCH /v1/linkedin/accounts/:accountId/settings

Mailboxes & senders 6

connect_client_mailboxConnect an own mailbox (SMTP/IMAP app password)newexecute

Connect the user's OWN mailbox for sending. Requires SMTP/IMAP hosts+ports and an APP PASSWORD, which passes through this conversation: prefer asking the user to connect it in the app (Settings › Email Sending) unless they explicitly give you the password. WARM-UP STARTS IMMEDIATELY: the real mailbox sends and receives warm-up mail. The password is never returned. Gmail: smtp.gmail.com:465 / imap.gmail.com:993; Outlook: smtp.office365.com:587 / outlook.office365.com:993.

ArgumentTypeReq.Description
emailstringyes
from_namestring
providerstringone of: google, microsoft, other
smtp_hoststringyes
smtp_portintegeryes
imap_hoststringyes
imap_portintegeryes
passwordstringyesApp password (never stored by Vendo; passed to the sending operator)

Calls: POST /v1/email/mailboxes

disconnect_client_mailboxDisconnect an own mailboxnewexecute

Stop using the mailbox as a sender. If none remain, client_owned campaigns cannot send or launch. Only after the user approved preview_disconnect_client_mailbox.

ArgumentTypeReq.Description
mailbox_idstringyes

Calls: DELETE /v1/email/mailboxes/:mailboxId

list_client_mailboxesOwn sending mailboxesnew

The workspace's own connected mailboxes (status connecting|warming|ready|error|disconnected, warm-up reputation, last error). Capacity ≈ 50 emails/day per READY box. Used when settings.emailSenderMode=client_owned (classic campaigns and SDR). Read-only.

No arguments.

Calls: GET /v1/email/mailboxes

list_sender_emailsEmail senders + capacitynew

Sending addresses available to this workspace (Vendo managed pool + own mailboxes) with warm-up level, daily limit and status; summary.emails_per_day_ready = capacity in managed mode. Managed senders require a paid plan to launch (summary.paid_plan). Use ids in settings.senderEmailIds. Read-only.

No arguments.

Calls: GET /v1/email/senders

preview_disconnect_client_mailboxPreview disconnecting an own mailboxnewpreview

Dry-run: which client_owned campaigns are affected and how many usable mailboxes remain. Nothing changes.

ArgumentTypeReq.Description
mailbox_idstringyes

Calls: DELETE /v1/email/mailboxes/:mailboxId

reconnect_client_mailboxReconnect an own mailboxnew

Retry the SMTP/IMAP connection of a mailbox in status error and refresh its status. No credits.

ArgumentTypeReq.Description
mailbox_idstringyes

Calls: POST /v1/email/mailboxes/:mailboxId/reconnect

Phone campaigns 4

activate_campaign_phoneActivate AI phone calls (REAL calls, credits per minute)newexecute

Schedule REAL outbound AI calls to every audience contact with a phone number; credits are charged per call minute. Same args as preview_activate_campaign_phone; only after the user approved that preview. Pause with pause_channel channel=phone_call.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
phone_number_idsstring[]yes
call_objectivestringone of: sell, inform, book_meeting, collect_info, qualify_lead
talking_pointsstring
max_calls_per_dayintegermin 1; max 1000
max_call_duration_secondsintegermin 30; max 1800
working_hours_startintegermin 0; max 23
working_hours_endintegermin 1; max 24
timezonestring
exclude_weekendsboolean

Calls: POST /v1/campaigns/:id/phone/activate

get_campaign_phone_statsPhone stats of a campaignnew

Call counters: total, completed, active, pending, no answer, voicemail, failed, interested, meetings booked, not interested, average/total duration. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/phone/stats

list_campaign_phone_callsCalls of a campaignnew

Calls with contact, status, outcome, duration, sentiment, summary and recording URL. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
statusstringone of: pending, scheduled, calling, completed, failed, skipped, no_answer, voicemail, busy
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/phone/calls

preview_activate_campaign_phonePreview AI phone calls for a campaignnewpreview

Dry-run of activating the phone channel with numbers the account already owns: numbers, objective, calls/day, window and estimated credits per call/day. Nothing is scheduled. Numbers cannot be bought through the API.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
phone_number_idsstring[]yesIds of the account's existing phone numbers
call_objectivestringDefault inform one of: sell, inform, book_meeting, collect_info, qualify_lead
talking_pointsstring
max_calls_per_dayintegerDefault 50 min 1; max 1000
max_call_duration_secondsintegerDefault 300 min 30; max 1800
working_hours_startintegerDefault: campaign working hours min 0; max 23
working_hours_endintegermin 1; max 24
timezonestring
exclude_weekendsboolean

Calls: POST /v1/campaigns/:id/phone/activate

Monitor, AI lead discovery & live stats 9

get_campaign_activityCampaign activity feednew

Paged activity events of a campaign (discovery, outreach, engagement, system, billing). Poll it instead of a live stream. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
categorystringone of: discovery, outreach, engagement, system, billing
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/activity

get_campaign_discoveryGet AI lead discovery confignew

AI lead discovery settings of a campaign (null when never configured). Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/discovery

get_campaign_email_statsCampaign email funnel incl. deliverynew

Email funnel: sent, delivered, opened, clicked, replied (real / auto-reply / bounce), bounced; delivery block (delivered vs not delivered: bounced, rejected as spam, unsubscribed; sender inbox-placement test, real inbox vs spam folder is not reported by providers) and unique opened/clicked/replied. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/email-stats

get_campaign_live_statusCampaign email progressnew

Email channel progress: audience size, processed, % complete, event counts, queue depth and provider status / sent today. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/live-status

get_campaign_monitorCampaign monitornew

Monitor page data: AI lead discovery config + stats, audience growth by day, the last 100 activity events, event counts and credits (balance, spent today/total, daily budget). Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns

Calls: GET /v1/campaigns/:id/monitor

get_campaign_pulseCampaign pulse (live status)new

What the campaign is doing now (sending / idle / outside_hours / paused / queue_empty / done), today vs daily limits, 7–28-day email + LinkedIn series and the latest events. Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
daysintegermin 7; max 28
limitintegerRecent events to return min 8; max 60

Calls: GET /v1/campaigns/:id/pulse

list_campaign_discovered_leadsLeads found by AI discoverynew

Leads found by AI lead discovery for a campaign, filterable by status (discovered, enriching, enriched, added_to_audience, rejected, duplicate, enrichment_failed). Read-only.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
statusstringone of: discovered, enriching, enriched, added_to_audience, rejected, duplicate, enrichment_failed
limitintegerPage size (default 50) min 1; max 200
offsetintegerRows to skip (default 0) min 0

Calls: GET /v1/campaigns/:id/discovery/leads

preview_update_campaign_discoveryPreview AI lead discovery settingsnewpreview

Dry-run of enabling/changing automatic lead discovery: source, daily credit budget, leads/day. Nothing changes. Call before update_campaign_discovery whenever discovery is (or stays) enabled.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
enabledboolean
search_sourcestringone of: apollo, signal
apollo_search_configobject
apollo_search_config.person_titlesstring[]
apollo_search_config.organization_keywordsstring[]
apollo_search_config.person_locationsstring[]
signal_search_configobject
signal_search_config.queriesstring[]
signal_search_config.linkedin_account_idstring
daily_discovery_budget_creditsintegermin 0
max_leads_per_dayintegermin 0
max_total_discoveredintegermin 0
search_interval_minutesintegermin 5
require_emailboolean

Calls: PATCH /v1/campaigns/:id/discovery

update_campaign_discoveryEnable / change AI lead discovery (SPENDS CREDITS)newexecute

Turn on or adjust automatic lead discovery: it runs daily, SPENDS CREDITS up to daily_discovery_budget_credits and adds new leads to the campaign audience. Same args as preview_update_campaign_discovery; only after the user approved that preview. Disabling (enabled=false) needs no preview.

ArgumentTypeReq.Description
idstringyesCampaign id (UUID) from list_campaigns
enabledboolean
search_sourcestringone of: apollo, signal
apollo_search_configobject
apollo_search_config.person_titlesstring[]
apollo_search_config.organization_keywordsstring[]
apollo_search_config.person_locationsstring[]
signal_search_configobject
signal_search_config.queriesstring[]
signal_search_config.linkedin_account_idstring
daily_discovery_budget_creditsintegermin 0
max_leads_per_dayintegermin 0
max_total_discoveredintegermin 0
search_interval_minutesintegermin 5
require_emailboolean

Calls: PATCH /v1/campaigns/:id/discovery

Content (social) accounts 4

connect_content_accountGet a link to connect a social publishing accountnew

Returns a hosted URL a HUMAN must open in a browser to authorise a LinkedIn or Instagram publishing account. Give the link to the user; nothing is connected until they finish.

ArgumentTypeReq.Description
providerstringyesone of: LINKEDIN, INSTAGRAM
redirect_pathstringApp path to return to, default /settings?tab=social

Calls: POST /v1/content/accounts/connect

disconnect_content_accountDisconnect a social publishing accountnewexecute

Remove the publishing account; it can no longer publish posts. Only after the user approved preview_disconnect_content_account.

ArgumentTypeReq.Description
account_idstringyes

Calls: DELETE /v1/content/accounts/:accountId

list_content_accountsSocial publishing accountsnew

LinkedIn / Instagram accounts connected for publishing posts (Settings › Social), with status. Read-only.

No arguments.

Calls: GET /v1/content/accounts

preview_disconnect_content_accountPreview disconnecting a social publishing accountnewpreview

Dry-run of removing a publishing account. Nothing changes.

ArgumentTypeReq.Description
account_idstringyes

Calls: DELETE /v1/content/accounts/:accountId

Audiences tools 31

People search & saved audiences 4

get_audienceGet one saved audience

Read one saved audience with its filters and contact count. Read-only, free.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/audiences/:id

list_audiencesList saved audiencesupdated

List the CAMPAIGN audiences (filter-based, attachable to campaigns) of this workspace. Uploaded / purchased / CRM-built lists ("My Audiences") are listed by list_custom_audiences. Read-only, free.

No arguments.

Calls: GET /v1/audiences

save_audienceSave an audienceupdated

Save a campaign audience from the same filter set as search_audience (call that first to check the counts). Returns the audience id to pass to create_campaign. No outreach happens here, no credits.

ArgumentTypeReq.Description
namestringAudience name (auto-generated when omitted)
filtersobjectyes
filters.countriesstring[]Country names, e.g. ["United Kingdom","Germany"]
filters.countrystring
filters.excludeCountriesstring[]
filters.citiesstring[]
filters.citystring
filters.excludedCitiesstring[]
filters.industriesstring[]
filters.excludedIndustriesstring[]
filters.positionsstring[]Job titles / roles, e.g. ["CEO","Head of Sales"]
filters.positionstring
filters.excludedPositionsstring[]
filters.companySizesstring[]items: startup, small, medium, large, enterprise
filters.companySizestringone of: startup, small, medium, large, enterprise
filters.excludedCompanySizesstring[]items: startup, small, medium, large, enterprise
filters.tagsstring[]
filters.companyKeywordsstring[]Keywords matched in company description / name / position
filters.languagestringApproximate language segment by country set, e.g. "russian"
filters.maxAgeDaysintegerOnly people added within the last N days min 1
filters.mustHaveEmailboolean
filters.mustHaveLinkedinboolean
filters.mustHavePhoneboolean
filters.mustHaveBookingUrlboolean
filters.searchModestringone of: keyword, exact, semantic
filters.semanticQuerystringFree-text description of the ideal person (searchMode=semantic)
filters.exactKeywordstringExact phrase (searchMode=exact)

Calls: POST /v1/audiences

search_audienceSearch the people databaseupdated

Search the people graph for prospects matching include AND exclude filters (countries, cities, industries, positions, company sizes, tags, company keywords, language, freshness, must-have-email/LinkedIn/phone/booking link). PREVIEW only, returns aggregate counts and a small sample. Does not save anything, does not contact anyone, does not spend credits. Next: save_audience with the same filters.

ArgumentTypeReq.Description
countriesstring[]Country names, e.g. ["United Kingdom","Germany"]
countrystring
excludeCountriesstring[]
citiesstring[]
citystring
excludedCitiesstring[]
industriesstring[]
excludedIndustriesstring[]
positionsstring[]Job titles / roles, e.g. ["CEO","Head of Sales"]
positionstring
excludedPositionsstring[]
companySizesstring[]items: startup, small, medium, large, enterprise
companySizestringone of: startup, small, medium, large, enterprise
excludedCompanySizesstring[]items: startup, small, medium, large, enterprise
tagsstring[]
companyKeywordsstring[]Keywords matched in company description / name / position
languagestringApproximate language segment by country set, e.g. "russian"
maxAgeDaysintegerOnly people added within the last N days min 1
mustHaveEmailboolean
mustHaveLinkedinboolean
mustHavePhoneboolean
mustHaveBookingUrlboolean
searchModestringone of: keyword, exact, semantic
semanticQuerystringFree-text description of the ideal person (searchMode=semantic)
exactKeywordstringExact phrase (searchMode=exact)
limitintegerSample size (max 100) min 1; max 100

Calls: POST /v1/audiences/search

Custom audiences (My Audiences) 19

add_custom_audience_contactsAdd contacts to a custom audiencenew

Append up to 5000 contacts to a custom audience. Deduplicated inside the audience by email, LinkedIn URL or phone (duplicates and contacts with no email/LinkedIn/phone are skipped). Returns added / skipped / total_contacts. No credits.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
contactsobject[]yesmin items 1; max items 5000
contacts[].first_namestring
contacts[].last_namestring
contacts[].emailstring
contacts[].phonestring
contacts[].linkedin_urlstring
contacts[].company_namestring
contacts[].positionstring

Calls: POST /v1/audiences/custom/:id/contacts

create_custom_audienceCreate an empty custom audiencenew

Create an empty, named custom audience (status ready), then fill it with add_custom_audience_contacts. To create one from a file use upload_custom_audience. No credits.

ArgumentTypeReq.Description
namestringyesmin length 1; max length 255

Calls: POST /v1/audiences/custom

delete_custom_audienceDelete a custom audiencenewexecute

Permanently delete a custom audience with all its contacts and source file. NOT reversible. Only call after the user approved preview_delete_custom_audience.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: DELETE /v1/audiences/custom/:id

delete_custom_audience_contactRemove a contact from a custom audiencenewexecute

Permanently remove one contact from a custom audience (not reversible). Optional reason (UI choices: Not relevant, Wrong contact, Already a customer, Bad data, Other) is logged to improve automatic selection. Only call after the user approved preview_delete_custom_audience_contact.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
contact_idstringyesContact id (UUID)
reasonstringmax length 200

Calls: DELETE /v1/audiences/custom/:id/contacts/:contactId

enrich_custom_audienceEnrich a custom audience (SPENDS CREDITS)newexecute

SPENDS CREDITS: starts background enrichment (email, phone, LinkedIn, WhatsApp lookups) of the not-yet-enriched contacts, 1 credit per contact actually enriched, charged as it runs. Only call after the user approved preview_enrich_custom_audience. Then poll get_custom_audience_enrichment; stop with stop_custom_audience_enrichment.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: POST /v1/audiences/custom/:id/enrich

export_custom_audience_csvExport a custom audience as CSVnew

Download every contact of a custom audience as CSV text (filename, total_rows, csv). Can be large. Read-only, free.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: GET /v1/audiences/custom/:id/export.csv

get_custom_audienceGet one custom audiencenew

One custom audience: status, counts (with email/phone/LinkedIn), enrichment progress, column mapping and the first 20 contacts. Page through all contacts with get_custom_audience_contacts. Read-only, free.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: GET /v1/audiences/custom/:id

get_custom_audience_contactsPage through custom audience contactsnew

Paged contacts of a custom audience (max 100 per page). Contacts added by Vendo SDR carry signal_context (why they were picked: reasoning, relevance score, ICP group). Read-only, free.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
pageintegermin 1
limitintegermin 1; max 100

Calls: GET /v1/audiences/custom/:id/contacts

get_custom_audience_enrichmentEnrichment progress of a custom audiencenew

Status and progress of a running (or finished) enrichment: status, enrichment_total, enrichment_done, enrichment_progress (%), done flag. Poll every 10–30 s. Read-only.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: GET /v1/audiences/custom/:id/enrichment

list_custom_audiencesList My Audiences (custom lists)new

List the workspace's custom audiences, uploaded CSV/XLSX lists, purchased audience sets, CRM selections, with status (processing|ready|enriching|error), contact counts and enrichment progress. These are different from campaign audiences (list_audiences). Read-only, free.

ArgumentTypeReq.Description
pageintegermin 1
limitintegerOmit to get all min 1; max 100

Calls: GET /v1/audiences/custom

prepare_campaign_audience_from_customUse a custom audience in a campaignnew

Turn a READY custom audience into a campaign audience and return its id ({ id, name, stats }), pass it as audienceId to create_campaign. Sends nothing, no credits. The "Launch campaign" button of My Audiences.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
namestringCampaign audience name (default: the custom audience name) max length 255

Calls: POST /v1/audiences/custom/:id/to-campaign-audience

preview_custom_audience_fileDetect columns of a contacts filenewexecute

Step 1 of an upload: send a CSV/XLSX/XLS/TSV/TXT file (base64 or https URL) and get the detected headers, a suggested column mapping, sample rows, total rows and the list of target fields. Creates nothing, no credits. Then call upload_custom_audience (optionally with a corrected column_mapping).

ArgumentTypeReq.Description
filenamestringFile name WITH extension (decides the type). Required with content_base64.
content_base64stringFile content, base64 (≤ ~7 MB). Use this OR file_url.
file_urlstringPublic https URL to fetch the file from (≤10 MB). Use this OR content_base64.

Calls: POST /v1/audiences/custom/preview

preview_delete_custom_audiencePreview deleting a custom audience (dry-run)newpreview

Dry-run: shows the audience name and how many contacts would be deleted. Changes nothing. ALWAYS call before delete_custom_audience and show the result.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: DELETE /v1/audiences/custom/:id

preview_delete_custom_audience_contactPreview removing a contact (dry-run)newpreview

Dry-run of removing one contact from a custom audience. Changes nothing. Show the result to the human before delete_custom_audience_contact.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
contact_idstringyesContact id (UUID)
reasonstring

Calls: DELETE /v1/audiences/custom/:id/contacts/:contactId

preview_enrich_custom_audiencePreview enriching a custom audience (cost)newpreview

Dry-run of enrichment: how many contacts still need enrichment, the maximum credits (1 per contact), estimated minutes, the current balance and whether it can start. Spends nothing. ALWAYS call first and show the cost to the human.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: POST /v1/audiences/custom/:id/enrich

rename_custom_audienceRename a custom audiencenew

Rename a custom audience. Reversible, no credits.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
namestringyesmin length 1; max length 255

Calls: PATCH /v1/audiences/custom/:id

stop_custom_audience_enrichmentStop a running enrichmentnew

Stop an in-progress enrichment after the current contact (stops further spending). Already-enriched contacts stay enriched and charged.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)

Calls: POST /v1/audiences/custom/:id/enrich/stop

update_custom_audience_contactEdit a contact in a custom audiencenew

Edit one contact of a custom audience (only the fields you pass change). No credits.

ArgumentTypeReq.Description
idstringyesCustom audience id (UUID)
contact_idstringyesContact id (UUID)
first_namestring
last_namestring
full_namestring
emailstring
phonestring
linkedin_urlstring
company_namestring
positionstring
countrystring
citystring
websitestring
whatsappstring
telegramstring
facebook_urlstring
instagram_urlstring

Calls: PATCH /v1/audiences/custom/:id/contacts/:contactId

upload_custom_audienceCreate a custom audience from a filenew

Create a custom audience from a CSV/XLSX/XLS/TSV/TXT file given as base64 or an https URL. Parsing runs in the background: poll get_custom_audience until status is "ready". No credits (enrichment is a separate, paid step).

ArgumentTypeReq.Description
filenamestringFile name WITH extension (decides the type). Required with content_base64.
content_base64stringFile content, base64 (≤ ~7 MB). Use this OR file_url.
file_urlstringPublic https URL to fetch the file from (≤10 MB). Use this OR content_base64.
namestringAudience name (default: file name) max length 255
column_mappingobject{ "<column index>": "<field>" } from preview_custom_audience_file (suggestedMapping / fieldOptions). Omit for auto-detect.

Calls: POST /v1/audiences/custom/upload

Audience Sets marketplace 8

buy_audience_setBuy an audience set

SPENDS CREDITS (2 per contact). Purchases the set and snapshots its contacts into the workspace. Only call after the user reviewed preview_buy_audience_set output and explicitly approved the cost.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/audiences/sets/:id/buy

get_audience_setGet one audience set

Details of one marketplace set by slug: size, description, freshness. Use the contact count here to work out the purchase cost (2 credits per contact). Read-only, free.

ArgumentTypeReq.Description
slugstringyesHuman-readable slug, not a UUID

Calls: GET /v1/audiences/sets/:slug

get_audience_set_contactOne contact card of an audience setnew

Full card of one contact in a marketplace set: person, company, buying signal, channels. Contact details stay masked until the set is bought. Contact ids come from get_audience_set_contacts. Read-only, free.

ArgumentTypeReq.Description
slugstringyesSet slug
contact_idstringyesContact id from get_audience_set_contacts

Calls: GET /v1/audiences/sets/:slug/contacts/:contactId

get_audience_set_contactsPreview contacts in an audience set

Paginated preview of the contacts inside a marketplace set. Contact details stay masked until the set is purchased. Read-only, free.

ArgumentTypeReq.Description
slugstringyes
offsetnumber
limitnumber

Calls: GET /v1/audiences/sets/:slug/contacts

launch_audience_setTurn an audience set into a campaign audienceupdatedexecute

Creates a campaign-ready audience from a marketplace set and returns its audienceId. Signals sets SPEND CREDITS (a fresh snapshot is bought, price per contact; it also appears in My Audiences); people sets are free. Does NOT contact anyone. Next: create_campaign with the audienceId, then preview_launch_campaign. Only call after the user approved preview_launch_audience_set.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/audiences/sets/:id/launch

list_audience_setsBrowse the Audience Sets marketplace

Browse pre-built, ready-to-use audiences available for purchase, plus their categories. Browsing is free; buying costs credits. Read-only.

No arguments.

Calls: GET /v1/audiences/sets

preview_buy_audience_setPreview buying an audience set (dry-run)updatedpreview

Dry-run of purchasing an audience set, returns the set, contact count, price per contact, estimated credits, current balance and can_afford, WITHOUT spending credits. ALWAYS call this first and show the cost to the human.

ArgumentTypeReq.Description
idstringyesSet id (UUID), not the slug

Calls: POST /v1/audiences/sets/:id/buy

preview_launch_audience_setPreview preparing a campaign audience from a set (dry-run)updatedpreview

Dry-run of launch_audience_set: shows the set, contact count, credits it would cost (signals sets: price per contact for a fresh snapshot; people sets: free) and the balance. Nothing is bought or sent. ALWAYS call this first and show the cost.

ArgumentTypeReq.Description
idstringyes

Calls: POST /v1/audiences/sets/:id/launch

CRM tools 53

Leads 18

crm_add_commentAdd an internal comment on a leadupdated

Internal team note on the lead card (not visible to the lead). Mentioned users (ids from crm_list_workspace_members) get a CRM notification.

ArgumentTypeReq.Description
idstringyes
bodystringyesmin length 1; max length 8000
mentionsstring[]User ids to @mention

Calls: POST /v1/crm/leads/:id/comments

crm_add_lead_to_branchAdd a lead to another pipelinenew

Place the lead in another pipeline too (entry stage); it stays in its current ones. Reversible via crm_move_lead_to_branch.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
branch_idstringyes"general" or a branch id from crm_list_all_branches
assignee_user_idstringOwner in that pipeline (default: current owner)

Calls: POST /v1/crm/leads/:id/branches

crm_create_leadCreate a lead manuallyupdated

Add a lead card to the entry stage of the chosen pipeline(s). ALL fields optional, provide whatever is known (at least one of name/company/title/email/linkedin/phone). Not deduplicated (use crm_import_leads or crm_promote_contact_to_lead for dedup). Side-effect: a card appears on the customer board.

ArgumentTypeReq.Description
first_namestring
last_namestring
company_namestring
titlestring
emailstring
linkedin_urlstringFull http(s) URL
phonestring
avatar_urlstringFull http(s) URL
instagram_urlstringFull http(s) URL
facebook_urlstringFull http(s) URL
assigned_user_idstringOwner, user id from crm_list_workspace_members (default: you)
notesstringmax length 8000
branch_idsstring[]Pipelines to place the lead in: "general" and/or branch ids (default General)

Calls: POST /v1/crm/leads

crm_enrich_from_linkedinEnrich a person from a LinkedIn URL

Fetch name / title / current employer / avatar / location for a linkedin.com/in/... URL (same lookup the SDR hunters use). May also suggest an email as an UNVERIFIED pattern guess (email_confidence=pattern_guess). Costs one profile view from the workspace's shared daily LinkedIn budget, do not loop over big lists.

ArgumentTypeReq.Description
linkedin_urlstringyes

Calls: POST /v1/crm/leads/enrich

crm_get_conversationGet lead conversation thread

The full message thread with this lead on their source channel (LinkedIn / email / widget / telegram / phone transcript). Read-only.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/crm/leads/:id/conversation

crm_get_leadGet one CRM lead (full detail)

Full lead card: identity, source ("why is this person here"), company enrichment, activities, comments, assignments. Read-only.

ArgumentTypeReq.Description
idstringyes

Calls: GET /v1/crm/leads/:id

crm_get_lead_branchesPipelines a lead is innew

Every pipeline a lead currently sits in, with its stage and owner per pipeline. Read-only.

ArgumentTypeReq.Description
idstringyesLead id (UUID)

Calls: GET /v1/crm/leads/:id/branches

crm_import_leadsImport leads into the CRMnew

Import up to 1000 parsed rows as leads into the chosen pipelines (entry stage). Dedup by email/phone: an existing lead only gets its blank fields filled (never overwritten) and is added to the pipelines. Returns created / updated / skipped (+ reasons). No credits.

ArgumentTypeReq.Description
rowsobject[]yesmin items 1; max items 1000
rows[].first_namestring
rows[].last_namestring
rows[].emailstring
rows[].phonestring
rows[].companystring
rows[].titlestring
rows[].linkedinstringLinkedIn URL
rows[].instagramstring
rows[].facebookstring
branch_idsstring[]"general" and/or branch ids (default General)

Calls: POST /v1/crm/leads/import

crm_list_leadsList CRM leadsupdated

List leads in the CRM pipeline. Filters: branch ("general" or a branch id from crm_list_branches; omitted = all branches visible to the user), stage (stage id), owner (user id from crm_list_workspace_members), source, q (name/company/email search), range or from/to, limit/offset. Read-only. Great first call for "who replied to me?".

ArgumentTypeReq.Description
branchstring
stagestring
ownerstringAssigned user id
sourcestringone of: linkedin_outreach_reply, linkedin_inbound, linkedin_manual, email_reply_real, widget_chat, widget_voice, widget_form, narrator_form, telegram_message, whatsapp_message, phone_call_interested, meeting_booked, crm_integration_inbound, manual
qstring
rangestringOmit for all time one of: 24h, 7d, 30d, 90d, 180d, 1y
fromstringISO date, created from
tostringISO date, created to
limitintegermin 1; max 200
offsetintegermin 0

Calls: GET /v1/crm/leads

crm_move_leadMove lead to another stage

Drag-and-drop equivalent: move a lead to a different pipeline stage by stage_id (get ids from crm_list_stages). Reversible.

ArgumentTypeReq.Description
idstringyes
stage_idstringyes

Calls: PATCH /v1/crm/leads/:id/stage

crm_move_lead_to_branchMove a lead between pipelinesnew

Move the lead out of one pipeline into another (entry stage, or to_stage_id of the destination). Reversible.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
from_branch_idstringyes"general" or the branch id it leaves
to_branch_idstringyes"general" or the destination branch id
to_stage_idstringA stage of the destination (crm_list_stages branch=<to>)
assignee_user_idstring

Calls: POST /v1/crm/leads/:id/branches/move

crm_preview_email_to_leadPreview an email to a lead (dry-run)newpreview

Dry-run of crm_send_email_to_lead: shows recipient, subject and body exactly as they would be sent. Sends nothing. ALWAYS call first and show the draft to the human.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
subjectstringyesmin length 1
bodystringyesPlain-text message min length 1
tostringRecipient (default: the lead's email)

Calls: POST /v1/crm/leads/:id/email

crm_reply_to_leadSend a reply to a lead (REAL outbound message)

Sends a REAL LinkedIn message or email reply to the lead on their source channel, from the customer's own account. Also moves the lead to the "Manager Dialog" stage. ALWAYS show the drafted text to the human and get approval BEFORE calling this.

ArgumentTypeReq.Description
idstringyes
textstringyesmin length 1

Calls: POST /v1/crm/leads/:id/reply

crm_send_email_to_leadSend a REAL email to a leadnewexecute

Sends a REAL email from the CRM card ("<your name> via Vendo AI", replies go to your own address) and logs it on the card. Members need the crm-contact-details permission. Only call after the user approved the exact draft from crm_preview_email_to_lead.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
subjectstringyesmin length 1
bodystringyesmin length 1
tostring

Calls: POST /v1/crm/leads/:id/email

crm_update_leadUpdate lead fieldsupdated

Patch lead attributes (name, company, title, email, LinkedIn, phone, notes, tags, deal value + currency). Stage and owner have their own tools (crm_move_lead, reassign_crm_lead). Reversible.

ArgumentTypeReq.Description
idstringyes
first_namestring
last_namestring
company_namestring
titlestring
emailstring
linkedin_urlstringFull http(s) URL
phonestring
notesstring
tagsstring[]Replaces the tag list
deal_valuenumber
currencystringISO code, e.g. USD max length 3

Calls: PATCH /v1/crm/leads/:id

delete_crm_leadDelete a CRM lead (soft)

Soft-delete a lead, it disappears from the pipeline (restorable by support only). Only call after the user reviewed preview_delete_crm_lead output and explicitly approved.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/crm/leads/:id

preview_delete_crm_leadPreview lead deletion (dry-run)preview

Dry-run of deleting a CRM lead, shows who would be removed from the board. ALWAYS call this first and show the result to the human.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/crm/leads/:id

reassign_crm_leadReassign a CRM lead to another ownerupdated

Change who owns a lead (the new owner gets a CRM notification). Call crm_list_workspace_members first to get a valid user id, passing an unknown id fails. Write operation, no credit cost.

ArgumentTypeReq.Description
idstringyesLead id
assignee_user_idstringyesNew owner, a user id from crm_list_workspace_members
reasonstringmax length 255

Calls: POST /v1/crm/leads/:id/reassign

Pipelines (branches) & stages 13

crm_create_branchCreate a CRM pipeline (branch)new

Create an isolated pipeline (team / direction / client) with its own default stages. member_user_ids = who can see it (General stays open to all). Workspace admins/owner only. No credits.

ArgumentTypeReq.Description
namestringyesmin length 1; max length 120
member_user_idsstring[]User ids from crm_list_workspace_members

Calls: POST /v1/crm/branches

crm_create_stageCreate a pipeline stageupdated

Add a custom kanban column to a pipeline (name, optional hex color, is_terminal for Won/Lost-like end stages). Inserted before the terminal stages. branch = "general" (default) or a branch id.

ArgumentTypeReq.Description
namestringyesmin length 1
colorstring#RRGGBB
is_terminalboolean
branchstring"general" (default) or a branch id

Calls: POST /v1/crm/pipeline-stages

crm_get_branchGet one CRM pipelinenew

One pipeline by id (id, name, is_default). Read-only.

ArgumentTypeReq.Description
idstringyesBranch id (UUID)

Calls: GET /v1/crm/branches/:id

crm_list_all_branchesList every CRM pipeline (for moves)new

Every pipeline of the workspace (id, name, is_default), wider than crm_list_branches for members; use it to pick a destination for crm_add_lead_to_branch / crm_move_lead_to_branch. Read-only.

No arguments.

Calls: GET /v1/crm/branches/all

crm_list_branchesList CRM branches (pipelines)

Branches are isolated pipelines inside one workspace (teams / directions / clients). Returns id, name, is_default ("General"), member_user_ids. Read-only. Call before crm_stats / crm_list_leads when the user has more than one branch, then pass branch=<id> (or "general").

No arguments.

Calls: GET /v1/crm/branches

crm_list_stagesList pipeline stagesupdated

Ordered kanban columns of ONE pipeline (id, name, color, is_terminal). Every branch has its own stages: pass branch ("general" = default, or a branch id from crm_list_branches). Read-only. Call before moving leads.

ArgumentTypeReq.Description
branchstring"general" (default) or a branch id

Calls: GET /v1/crm/pipeline-stages

crm_reorder_stagesReorder pipeline stagesnew

Set the full left-to-right order of one pipeline's stages (every stage id exactly once, get them from crm_list_stages with the same branch). Applies to the whole workspace. Reversible.

ArgumentTypeReq.Description
stage_idsstring[]yesAll stage ids of the branch in the new order min items 1
branchstring"general" (default) or a branch id

Calls: POST /v1/crm/pipeline-stages/reorder

crm_update_branchRename a pipeline / change its membersnew

Rename a pipeline and/or replace its member list. The General pipeline cannot be changed. Admins/owner only.

ArgumentTypeReq.Description
idstringyesBranch id (UUID)
namestringmin length 1; max length 120
member_user_idsstring[]Replaces the member list

Calls: PATCH /v1/crm/branches/:id

crm_update_stageRename / recolor a pipeline stageupdated

Rename, recolor or toggle is_terminal of a stage. System stages (New, Manager Dialog) cannot be renamed. To change the column order use crm_reorder_stages.

ArgumentTypeReq.Description
idstringyes
namestring
colorstring#RRGGBB
is_terminalboolean

Calls: PATCH /v1/crm/pipeline-stages/:id

delete_crm_branchDelete a pipelinenewexecute

Delete a non-default pipeline; its leads are not deleted but return to General. NOT reversible. Admins/owner only. Only call after the user approved preview_delete_crm_branch.

ArgumentTypeReq.Description
idstringyesBranch id (UUID)

Calls: DELETE /v1/crm/branches/:id

delete_crm_stageDelete a pipeline stage

Delete an EMPTY, non-system pipeline stage. Only call after the user reviewed preview_delete_crm_stage and explicitly approved.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/crm/pipeline-stages/:id

preview_delete_crm_branchPreview deleting a pipeline (dry-run)newpreview

Dry-run: shows the pipeline and how many leads would fall back to General. Changes nothing. ALWAYS call before delete_crm_branch.

ArgumentTypeReq.Description
idstringyesBranch id (UUID)

Calls: DELETE /v1/crm/branches/:id

preview_delete_crm_stagePreview stage deletion (dry-run)preview

Dry-run of deleting a pipeline stage. Deletion is refused downstream if the stage still holds leads or is a system stage.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/crm/pipeline-stages/:id

Lead documents 5

crm_get_lead_documentDownload a lead documentnew

Returns the file as base64 with its name and mime type (files up to 5 MB; larger ones only via REST). Read-only.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
document_idstringyesDocument id (UUID)

Calls: GET /v1/crm/leads/:id/documents/:docId/file

crm_list_lead_documentsList documents on a leadnew

Files attached to a lead card (id, name, type, size, uploader, date). Read-only.

ArgumentTypeReq.Description
idstringyesLead id (UUID)

Calls: GET /v1/crm/leads/:id/documents

crm_upload_lead_documentAttach a document to a leadnew

Attach one file to a lead card (base64 or https URL). Allowed: pdf, doc(x), xls(x), csv, txt, rtf, odt, ods, ppt(x), png, jpg, gif, webp, heic, zip. Stored privately; adds an activity entry. No credits.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
filenamestringFile name WITH extension (decides the type). Required with content_base64.
content_base64stringFile content, base64 (≤ ~7 MB). Use this OR file_url.
file_urlstringPublic https URL to fetch the file from (≤10 MB). Use this OR content_base64.

Calls: POST /v1/crm/leads/:id/documents

delete_crm_lead_documentDelete a lead documentnewexecute

Permanently delete a file from a lead card. NOT reversible. Only call after the user approved preview_delete_crm_lead_document.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
document_idstringyesDocument id (UUID)

Calls: DELETE /v1/crm/leads/:id/documents/:docId

preview_delete_crm_lead_documentPreview deleting a lead document (dry-run)newpreview

Dry-run: shows which file would be deleted. Changes nothing.

ArgumentTypeReq.Description
idstringyesLead id (UUID)
document_idstringyesDocument id (UUID)

Calls: DELETE /v1/crm/leads/:id/documents/:docId

Communications 4

crm_communication_cardsList communication cardsupdated

One card per campaign and per SDR search, with outbound counters. This is the FULL outbound stream, everyone contacted, whether or not they replied (CRM leads only contains people who answered). Contact emails/phones are masked for members without the "crm-contact-details" permission. Read-only, free.

ArgumentTypeReq.Description
limitintegermin 1; max 500

Calls: GET /v1/crm/communications/cards

crm_communication_historyOutbound history for one cardupdated

Every outbound touch for one campaign or SDR search, filterable by channel, reply status and date. Use crm_communication_cards first to get the id. Read-only, free.

ArgumentTypeReq.Description
typestringyesone of: campaign, sdr
idstringyesCampaign id or SDR search id
channelstringone of: email, linkedin, phone
repliedboolean
fromstringISO date
tostringISO date
limitnumber
offsetnumber

Calls: GET /v1/crm/communications/cards/:type/:id/history

crm_communication_threadFull message thread for one contactupdated

The complete message thread with one contact inside a card. key comes from a crm_communication_history row (an email address or LinkedIn chat id). Read-only, free.

ArgumentTypeReq.Description
typestringyesone of: campaign, sdr
idstringyes
keystringyesThread key from crm_communication_history

Calls: GET /v1/crm/communications/cards/:type/:id/thread

crm_promote_contact_to_leadPromote a contacted person to a CRM leadnew

Turn a person from the Communications stream (crm_communication_history row) into a CRM lead in the New stage. Deduplicated: returns { lead_id, already_existed }. Needs at least one of email / linkedin_url / phone. No credits.

ArgumentTypeReq.Description
emailstring
linkedin_urlstring
phonestring
contact_namestring
company_namestring
titlestring
avatar_urlstring
assigned_user_idstringOwner (default: you)

Calls: POST /v1/crm/communications/promote

Calendar 4

crm_create_calendar_eventCreate a CRM calendar eventupdated

Add a custom event to the CRM planner. Write operation, no credit cost. Does NOT send invites to anyone, it only records the event on the internal calendar.

ArgumentTypeReq.Description
titlestringyes
start_atstringyesISO datetime
end_atstringISO datetime, after start_at (default start + 1 h)
all_dayboolean
descriptionstring
colorstring#RRGGBB pattern ^#[0-9a-fA-F]{6}$
lead_idstringLink the event to a CRM lead

Calls: POST /v1/crm/calendar/events

crm_delete_calendar_eventDelete a CRM calendar event

Remove a custom calendar event. Unlike deleting a lead or a stage this destroys no history and needs no preview step, but it cannot be undone.

ArgumentTypeReq.Description
idstringyes

Calls: DELETE /v1/crm/calendar/events/:id

crm_list_calendar_eventsList CRM calendar events

List custom calendar events (meetings, reminders) in the CRM planner for a date range. Note: SDR sending windows also appear in the UI calendar but are derived from the search schedule, read those via sdr_get_search. Read-only, free.

ArgumentTypeReq.Description
fromstringISO date, start of range
tostringISO date, end of range

Calls: GET /v1/crm/calendar/events

crm_update_calendar_eventUpdate a CRM calendar eventupdated

Edit an existing custom calendar event (title, times, all-day, description, colour, linked lead, lead_id null unlinks). Write operation, no credit cost.

ArgumentTypeReq.Description
idstringyes
titlestring
start_atstringISO datetime
end_atstringISO datetime
all_dayboolean
descriptionstring
colorstring#RRGGBB pattern ^#[0-9a-fA-F]{6}$
lead_idstring | nullLead id, or null to unlink

Calls: PATCH /v1/crm/calendar/events/:id

CRM notifications 3

crm_list_notificationsList CRM notificationsupdated

Latest 50 CRM notifications of the caller (types: assigned, reassigned, mentioned, new_lead, stage_change) plus total and unread counts. unread_only=true returns only unread ones. For the app bell (billing, meetings, invitations) use list_app_notifications. Read-only, free.

ArgumentTypeReq.Description
unread_onlyboolean

Calls: GET /v1/crm/notifications

crm_mark_all_notifications_readMark all notifications as read

Clear the CRM notification badge by marking everything read.

No arguments.

Calls: POST /v1/crm/notifications/read-all

crm_mark_notification_readMark one notification as read

Mark a single CRM notification as read. Write operation, no credit cost.

ArgumentTypeReq.Description
idstringyes

Calls: PATCH /v1/crm/notifications/:id/read

Settings & lookups 6

crm_get_settingsGet CRM settings

Read CRM settings, including the notify_* toggles that control which events send an email/notification. Read-only, free.

No arguments.

Calls: GET /v1/crm/settings

crm_list_linkedin_accountsList connected LinkedIn accounts

List the LinkedIn accounts connected to this workspace, as shown in the CRM. Useful to see which account owns a conversation. Read-only, free.

No arguments.

Calls: GET /v1/crm/linkedin-accounts

crm_list_workspace_agentsList agents visible in CRM

List the AI agents in this workspace that CRM leads can be attributed to. Read-only, free.

No arguments.

Calls: GET /v1/crm/agents

crm_list_workspace_membersList workspace members

List the people in this workspace who can own a CRM lead. Use before reassign_crm_lead to get valid owner ids. Read-only, free.

No arguments.

Calls: GET /v1/crm/workspace-members

crm_statsCRM pipeline stats

Counts per stage, new today/this week, stale leads. Read-only. Optional branch ("general" or a branch id from crm_list_branches), omitted = all branches visible to the user. If the workspace has more than one branch, call crm_list_branches first and report per branch or ask which one.

ArgumentTypeReq.Description
branchstring

Calls: GET /v1/crm/stats

crm_update_settingsUpdate CRM settingsupdated

Change workspace CRM settings: which notifications fire (new lead, reassigned, mention; by email and/or in-app), the stale-lead alert, the default date range, and which agents / sources / LinkedIn accounts never create leads. Write operation, no credit cost. Turning notifications off or excluding sources means the human stops being alerted / leads stop appearing, so confirm with the user first. (Personal email preferences: update_notification_preferences.)

ArgumentTypeReq.Description
notify_new_leadboolean
notify_reassignedboolean
notify_mentionboolean
notify_emailbooleanSend CRM notifications by email
notify_in_appbooleanShow CRM notifications in the bell
stale_alert_daysintegermin 1; max 90
default_date_rangestringone of: 24h, 7d, 30d, 90d, 180d, 1y, all
excluded_agent_idsstring[]Agents whose conversations never create leads (ids from crm_list_workspace_agents). Replaces the list.
excluded_sourcesstring[]Lead sources to ignore, e.g. ["widget_form"]. Replaces the list.
excluded_linkedin_account_idsstring[]From crm_list_linkedin_accounts. Replaces the list.

Calls: PATCH /v1/crm/settings

Workspace & account tools 34

Team 9

cancel_workspace_invitationCancel a pending invitationnew

Cancel a pending invitation (its link stops working). Admins only. Invitation ids come from get_workspace.pendingInvitations.

ArgumentTypeReq.Description
idstringyesInvitation id (UUID)

Calls: DELETE /v1/workspace/invitations/:id

get_workspaceGet workspace, members and invitationsnew

Workspace name/owner, members (user_id, email, name, role, feature permissions), pending invitations, and whether the caller is admin/owner. Read-only.

No arguments.

Calls: GET /v1/workspace

invite_workspace_memberInvite a teammate (sends an email)newexecute

Sends a REAL invitation email (link valid 7 days). Admins only. Only call after the user approved preview_invite_workspace_member.

ArgumentTypeReq.Description
emailstringyes

Calls: POST /v1/workspace/invitations

preview_invite_workspace_memberPreview inviting a teammate (dry-run)newpreview

Dry-run: checks whether the email can be invited (caller is admin, not already a member, no pending invite). Sends nothing.

ArgumentTypeReq.Description
emailstringyes

Calls: POST /v1/workspace/invitations

preview_remove_workspace_memberPreview removing a teammate (dry-run)newpreview

Dry-run: shows who would lose access. Changes nothing. ALWAYS call before remove_workspace_member.

ArgumentTypeReq.Description
user_idstringyes

Calls: DELETE /v1/workspace/members/:userId

preview_set_workspace_member_rolePreview promoting/demoting an admin (dry-run)newpreview

Dry-run of set_workspace_member_role. Changes nothing.

ArgumentTypeReq.Description
user_idstringyes
adminbooleanyes

Calls: PATCH /v1/workspace/members/:userId/role

remove_workspace_memberRemove a teammatenewexecute

Remove a member: they immediately lose access to this workspace (their account gets its own empty workspace). Re-adding needs a new invitation. Admins only. Only call after the user approved preview_remove_workspace_member.

ArgumentTypeReq.Description
user_idstringyes

Calls: DELETE /v1/workspace/members/:userId

set_workspace_member_permissionsSet a member's feature accessnew

Restrict what a (non-admin) member sees: an allow-list of features, or null for full access. "crm-contact-details" controls whether they see lead emails/phones. Admins only; the owner and admins cannot be restricted.

ArgumentTypeReq.Description
user_idstringyesMember user_id from get_workspace
permissionsstring[] | nullyesAllowed features, or null = full access items: dashboard, my-agents, sales-process, vendo-sdr, audiences, crm, analytics, billing, settings, crm-contact-details

Calls: PATCH /v1/workspace/members/:userId/permissions

set_workspace_member_rolePromote / demote a workspace adminnewexecute

admin=true makes the member an admin (full access, can manage team and pipelines); false demotes to member. Owner only. Only call after the user approved preview_set_workspace_member_role.

ArgumentTypeReq.Description
user_idstringyes
adminbooleanyes

Calls: PATCH /v1/workspace/members/:userId/role

Profile & preferences 5

get_notification_preferencesGet my notification preferencesnew

Personal email/in-app preferences: email_notifications, in_app_notifications, meeting_reminders, credit_alerts, analytics_emails_enabled. Read-only.

No arguments.

Calls: GET /v1/me/notification-preferences

get_profileGet my profilenew

The API key owner's profile: name, email, company, position, avatar. Read-only.

No arguments.

Calls: GET /v1/me

set_profile_avatarSet my profile photonew

Upload a JPG/PNG/GIF (≤5 MB) as the profile photo, from an https URL or base64.

ArgumentTypeReq.Description
image_urlstringPublic https image URL
content_base64string
filenamestringe.g. me.png (required with content_base64)

Calls: PATCH /v1/me/avatar

update_notification_preferencesUpdate my notification preferencesnew

Turn personal notifications on/off. Turning them off means the human stops being alerted (e.g. low credits, meetings), confirm with the user first.

ArgumentTypeReq.Description
email_notificationsboolean
in_app_notificationsboolean
meeting_remindersboolean
credit_alertsboolean
analytics_emails_enabledbooleanPeriodic analytics report emails

Calls: PATCH /v1/me/notification-preferences

update_profileUpdate my profilenew

Change first_name, last_name, company_name, position of the API key owner. (Email, password and 2FA are UI-only.)

ArgumentTypeReq.Description
first_namestringmin length 1; max length 100
last_namestringmax length 100
company_namestringmax length 200
positionstringmax length 200

Calls: PATCH /v1/me

In-app notifications 5

delete_app_notificationDelete an app notificationnew

Remove one in-app notification from the caller's list.

ArgumentTypeReq.Description
idstringyesNotification id (UUID)

Calls: DELETE /v1/notifications/:id

get_app_notifications_unread_countUnread app notifications countnew

Number of unread in-app notifications (the bell badge). Read-only.

No arguments.

Calls: GET /v1/notifications/unread-count

list_app_notificationsList app notifications (bell)new

The caller's in-app notifications, newest first (type, subject, preview, isRead, createdAt), with unreadCount and paging. Types include MEETING_TRANSCRIPT, MEETING_NOTIFICATION, SUBSCRIPTION_*, PAYMENT_RECEIPT, LOW_CREDITS, CREDITS_ADDED, INVITATION, CUSTOM. CRM lead notifications are separate (crm_list_notifications). Read-only.

ArgumentTypeReq.Description
pageintegermin 1
limitintegermin 1; max 100

Calls: GET /v1/notifications

mark_all_app_notifications_readMark all app notifications readnew

Mark every in-app notification of the caller as read (clears the badge).

No arguments.

Calls: POST /v1/notifications/read-all

mark_app_notification_readMark an app notification readnew

Mark one in-app notification as read.

ArgumentTypeReq.Description
idstringyesNotification id (UUID)

Calls: POST /v1/notifications/:id/read

CRM integrations 10

connect_crm_integrationStart connecting Kommo / HubSpot (OAuth)new

Returns an authorization URL a HUMAN must open in a browser (logged in to Kommo/HubSpot) and approve within 20 minutes; the connection completes server-side. Give the URL to the user, then poll get_crm_integration. Kommo needs save_kommo_credentials first.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot

Calls: POST /v1/integrations/:provider/connect

disconnect_crm_integrationDisconnect a CRM integrationnewexecute

Remove the Kommo/HubSpot connection: syncing stops, tokens and settings are deleted; reconnecting needs OAuth again. Only call after the user approved preview_disconnect_crm_integration.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot

Calls: DELETE /v1/integrations/:provider

get_crm_integrationGet one CRM integrationnew

Status and settings of the Kommo or HubSpot connection ({connected:false} if none). Use it to poll after connect_crm_integration. Read-only.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot

Calls: GET /v1/integrations/:provider

get_crm_integration_logsCRM integration sync lognew

Recent sync events (contact/lead created, errors) of the caller's own Kommo/HubSpot connection. Read-only.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot
limitintegermin 1; max 200

Calls: GET /v1/integrations/:provider/logs

list_crm_integrationsList CRM integrationsnew

External CRM connections of the workspace (Kommo, HubSpot): status, account, sync settings and counters. Tokens are never returned. Read-only.

No arguments.

Calls: GET /v1/integrations

list_kommo_pipelinesList Kommo pipelinesnew

Pipelines and their statuses (stages) in the connected Kommo account, values for pipeline_config. Read-only.

No arguments.

Calls: GET /v1/integrations/kommo/pipelines

list_kommo_usersList Kommo usersnew

Users of the connected Kommo account, values for pipeline_config.responsible_user_id. Read-only.

No arguments.

Calls: GET /v1/integrations/kommo/users

preview_disconnect_crm_integrationPreview disconnecting a CRM (dry-run)newpreview

Dry-run: shows which connection would be removed. Changes nothing.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot

Calls: DELETE /v1/integrations/:provider

save_kommo_credentialsSave Kommo app credentialsnew

Kommo private integration step 1: store the Integration ID (UUID) and Secret key of the Vendo app the client created in their Kommo account. The secret is never echoed back. Returns the redirect_uri to paste into Kommo. Then connect_crm_integration provider=kommo.

ArgumentTypeReq.Description
client_idstringyesKommo Integration ID (UUID)
client_secretstringyesKommo Secret key

Calls: POST /v1/integrations/kommo/credentials

update_crm_integration_settingsUpdate CRM integration settingsnew

What to sync and where: sync_conversations, sync_contacts, create_leads, auto_sync, pipeline_config (Kommo pipeline_id / stage_id / responsible_user_id from list_kommo_pipelines / list_kommo_users), field_mapping. Affects what is written into the external CRM, confirm with the user.

ArgumentTypeReq.Description
providerstringyesone of: kommo, hubspot
sync_conversationsboolean
sync_contactsboolean
create_leadsboolean
auto_syncboolean
pipeline_configobject
pipeline_config.pipeline_idstring
pipeline_config.stage_idstring
pipeline_config.responsible_user_idstring
field_mappingobject

Calls: PATCH /v1/integrations/:provider/settings

Referral program 5

get_referral_overviewReferral program overviewnew

Commission %, minimum payout, totals (visits, signups, paying customers, revenue, earned, paid out, available), every referral link with its stats, and payout request statuses. Payout requests themselves can only be made in the app. Read-only.

No arguments.

Calls: GET /v1/referral

get_referral_share_cardReferral share card datanew

Everything needed to render the share card / QR for a link: url (QR payload), short url, headline and description texts. Render the QR image yourself. Read-only.

ArgumentTypeReq.Description
idstringyesReferral link id (UUID)

Calls: GET /v1/referral/links/:id/share

list_referred_usersList referred usersnew

People who signed up through the caller's referral links (masked email, link, signup date, free/paying, revenue and commission). Read-only.

No arguments.

Calls: GET /v1/referral/referred-users

Analytics & planner tools 4

Analytics 3

get_analytics_agentsAgents analytics (page /analytics/agents)new

All metrics of the workspace's agents for a UTC date range (default: the page default), optionally one agent: totals, rates, daily series, funnel (widget loads → opens → conversations → contacts → leads), by agent / channel, leads by source, recent leads, credits by type, languages, pages, traffic sources, hours, durations, previous-period comparison. Read-only.

ArgumentTypeReq.Description
fromstringYYYY-MM-DD
tostringYYYY-MM-DD
agent_idstring

Calls: GET /v1/analytics/agents

get_analytics_sdrSDR & outreach analytics (page /analytics/sdr)new

Outreach analytics for a UTC date range across Vendo SDR and classic campaigns, or narrowed to one SDR search (search_id) or one campaign (campaign_id), never both: totals, rates, daily series, delivery (sent/delivered/bounced/inbox placement), by product / source / type, contacts by source, enrichment, funnel, per-process rows. Read-only.

ArgumentTypeReq.Description
fromstringYYYY-MM-DD
tostringYYYY-MM-DD
search_idstring
campaign_idstring

Calls: GET /v1/analytics/sdr

get_campaign_analyticsCampaign analytics (all campaigns)new

Sales Process KPIs for the workspace: totals (campaigns, emails sent, opens, clicks, replies, contacts, rates) and per-campaign performance, optionally for a date range. Read-only. For SDR + campaigns combined use the SDR analytics tool.

ArgumentTypeReq.Description
fromstringYYYY-MM-DD
tostringYYYY-MM-DD

Calls: GET /v1/analytics/campaigns

Planner 1

get_planner_eventsPlanner: sending windows per daynew

The planner layer of the calendar page: campaign and SDR sending windows per day and channel (paused flag, planned today, sent today) with campaign/search info, for an ISO date range (wide ranges are capped; range says what was materialised). CRM events: crm_list_calendar_events. Read-only.

ArgumentTypeReq.Description
fromstringyesISO date
tostringyesISO date

Calls: GET /v1/planner/events

Billing (read-only) tools 14

Billing 14

get_balanceGet credit balanceupdated

Get the current shared credit balance of the workspace (credits belong to the workspace owner; member keys see the same number). Read-only, no side-effects, no cost.

No arguments.

Calls: GET /v1/billing/balance

get_balance_fullGet full billing balance + plan summary

Richer than get_balance: returns balance, total subscription credits, total purchased, total spent, free-trial credits, email/workspace info.

No arguments.

Calls: GET /v1/billing/balance/full

get_credit_transactionsGet credit transaction historyupdated

Credit deductions + grants of the workspace, newest first. Optional filters: type, date range (from/to, a date-only to is inclusive), pagination. With filters the gateway scans the newest 5000 rows; has_more/total_matched tell you if there is more. Read-only.

ArgumentTypeReq.Description
typestringTransaction type, e.g. SUBSCRIPTION_GRANT, PURCHASE, FREE_TRIAL, BONUS, REFUND, EMAIL_OUTREACH, LINKEDIN_INVITE, LINKEDIN_MESSAGE, SDR_CONTACT, PHONE_CALL, VOICE_MINUTE, TEXT_MESSAGE, AUDIENCE_ENRICHMENT, AUDIENCE_SET_CONTACT, DISCOVERY_ENRICH, ASSISTANT_MESSAGE, SCRIPT_GENERATION, IMAGE_GENERATION
fromstringISO date, e.g. 2026-06-01
tostringISO date, e.g. 2026-06-30
limitintegermin 1; max 200
offsetintegermin 0

Calls: GET /v1/billing/transactions

get_credit_usage_by_agentGet credit spend per agentupdated

Total credit spend per agent in the workspace. Identifies the most expensive agents.

No arguments.

Calls: GET /v1/billing/usage/by-agent

get_credit_usage_by_typeGet credit spend by type / sessionupdated

Per-session credit spend (newest first) with connectionType (email_outreach, linkedin_invite, linkedin_message, phone_call, widget, voice, sdr_contact, ...). With from/to it also returns totals_by_type for that range. Read-only.

ArgumentTypeReq.Description
fromstringISO date, start of range
tostringISO date, end of range (date-only = inclusive)
limitintegermin 1; max 500
offsetintegermin 0

Calls: GET /v1/billing/usage/by-type

get_credit_usage_for_agentGet credit spend for a single agent

Total credits spent by one specific agent.

ArgumentTypeReq.Description
agent_idstringyes

Calls: GET /v1/billing/usage/agents/:agentId

get_credit_usage_for_agent_connectionsGet credit spend by connection for an agent

Per-channel credit spend (widget, telegram, phone, etc.) for one agent. Identifies the costliest channels.

ArgumentTypeReq.Description
agent_idstringyes

Calls: GET /v1/billing/usage/agents/:agentId/connections

get_credit_usage_monthlyGet credit spend per monthupdated

Calendar-month breakdown of credit spend across all categories.

No arguments.

Calls: GET /v1/billing/usage/monthly

get_invoicesGet Stripe invoices

List of Stripe invoices (paid + open). Optional year/month scope.

ArgumentTypeReq.Description
yearinteger
monthintegermin 1; max 12

Calls: GET /v1/billing/invoices

get_pricingGet public pricing catalogueupdated

List of public plans + add-ons (Extra Agent Slot, SDR sender pair, etc.) with prices. Optional currency filter (USD/GBP/EUR); omitted = every currency. Read-only.

ArgumentTypeReq.Description
currencystringOnly prices in this currency one of: USD, GBP, EUR

Calls: GET /v1/billing/pricing

get_sdr_email_capacityGet Vendo SDR managed-sender capacity

How many SDR sender email addresses the account has (included + purchased extras) and the estimated daily email throughput. Read-only.

No arguments.

Calls: GET /v1/billing/sdr/email-capacity

get_subscriptionGet current subscription

Plan, status (ACTIVE/CANCELLED/PAST_DUE/EXPIRED/TRIALING), period dates, currency, amount, Stripe IDs.

No arguments.

Calls: GET /v1/billing/subscription

get_subscription_limitsGet plan limits (max agents, extra slots)updated

Base + purchased extra agent slots. Useful to check "can I create another agent?" before calling create_agent.

No arguments.

Calls: GET /v1/billing/subscription/limits

get_supported_currenciesSupported billing currenciesnew

Currencies Vendo bills in (code, symbol, name) and the account's own billing currency. Read-only.

No arguments.

Calls: GET /v1/billing/currencies

Guides

Complete, runnable flows. Each block works on its own: set VENDO_API_KEY (the curl versions use jq; JavaScript needs Node 18+ as an ES module; Python needs requests). Steps that spend credits or contact people are called out; try them with a vnd_test_… key first, writes are then refused, previews still work.

Create and launch an AI agent on a website widget

Create the agent, give it company details, generate the script (a job), set contact channels, activate it and add the WEBSITE_WIDGET channel. To read the company website automatically instead of writing the description, use POST /v1/agents/:id/deep-scan (15 credits, dry_run → confirm).

curl
H=(-H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json")
API=https://api.vendo-ai.com/v1

# 1. Create the agent (starts in DRAFT, contacts nobody)
AGENT_ID=$(curl -s -X POST $API/agents "${H[@]}" \
  -d '{"name":"Anna","description":"Answers questions about Acme CRM and books demos","purpose":"sales","gender":"female"}' \
  | jq -r .data.id)

# 2. Company details the script is written from
curl -s -X PATCH $API/agents/$AGENT_ID/company "${H[@]}" -d '{
  "company_name": "Acme", "company_website": "https://acme.com",
  "company_description": "Acme builds a CRM for small sales teams. Plans from $29/month."}'

# 3. Generate the script (1 credit, 60-90 s). 202 → poll the job.
R=$(curl -s -X POST $API/agents/$AGENT_ID/generate-script "${H[@]}" -d '{"script_style":"FRIENDLY_CASUAL"}')
JOB=$(echo "$R" | jq -r .data.job_id)
while [ "$(echo "$R" | jq -r .data.status)" = "running" ]; do
  sleep 15; R=$(curl -s $API/agents/$AGENT_ID/jobs/$JOB "${H[@]}")
done

# 4. Where visitors can book / reach you
curl -s -X PATCH $API/agents/$AGENT_ID/contact-channels "${H[@]}" \
  -d '{"meeting_url":"https://calendly.com/acme/demo","email":"sales@acme.com"}'

# 5. Activate the agent and add the website widget channel
curl -s -X PATCH $API/agents/$AGENT_ID/status "${H[@]}" -d '{"status":"ACTIVE"}'
CID=$(curl -s -X POST $API/agents/$AGENT_ID/connections "${H[@]}" -d '{
  "type":"WEBSITE_WIDGET","website_url":"acme.com",
  "widget_config":{"teaser":true,"collectName":true,"collectEmail":true,
                   "texts":{"welcomeMessage":"Hi! I am Anna from Acme."}}}' | jq -r .data.id)

# 6. Embed code — paste before </body> on your site
curl -s $API/agents/$AGENT_ID/connections/$CID/embed-code "${H[@]}" | jq -r .data.embed_code
JavaScript
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data; // 202 (job still running) also lands here
}

const agent = await vendo("POST", "/agents", {
  name: "Anna", description: "Answers questions about Acme CRM and books demos",
  purpose: "sales", gender: "female",
});
await vendo("PATCH", `/agents/${agent.id}/company`, {
  company_name: "Acme", company_website: "https://acme.com",
  company_description: "Acme builds a CRM for small sales teams. Plans from $29/month.",
});

// Script generation is a job: 200 with the result, or 202 → poll
let job = await vendo("POST", `/agents/${agent.id}/generate-script`, { script_style: "FRIENDLY_CASUAL" });
while (job.status === "running") {
  await sleep(15000);
  job = await vendo("GET", `/agents/${agent.id}/jobs/${job.job_id ?? job.id}`);
}
if (job.status === "error") throw new Error(JSON.stringify(job.error));

await vendo("PATCH", `/agents/${agent.id}/contact-channels`, {
  meeting_url: "https://calendly.com/acme/demo", email: "sales@acme.com",
});
await vendo("PATCH", `/agents/${agent.id}/status`, { status: "ACTIVE" });

const channel = await vendo("POST", `/agents/${agent.id}/connections`, {
  type: "WEBSITE_WIDGET", website_url: "acme.com",
  widget_config: { teaser: true, collectName: true, collectEmail: true,
                   texts: { welcomeMessage: "Hi! I am Anna from Acme." } },
});
const embed = await vendo("GET", `/agents/${agent.id}/connections/${channel.id}/embed-code`);
console.log(embed.embed_code); // paste before </body>
Python
import os, time, requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]   # 202 (job still running) also lands here

agent = vendo("POST", "/agents", {
    "name": "Anna", "description": "Answers questions about Acme CRM and books demos",
    "purpose": "sales", "gender": "female"})
aid = agent["id"]
vendo("PATCH", f"/agents/{aid}/company", {
    "company_name": "Acme", "company_website": "https://acme.com",
    "company_description": "Acme builds a CRM for small sales teams. Plans from $29/month."})

job = vendo("POST", f"/agents/{aid}/generate-script", {"script_style": "FRIENDLY_CASUAL"})
while job["status"] == "running":           # 202 → poll the job
    time.sleep(15)
    job = vendo("GET", f"/agents/{aid}/jobs/{job.get('job_id') or job['id']}")
assert job["status"] == "done", job

vendo("PATCH", f"/agents/{aid}/contact-channels",
      {"meeting_url": "https://calendly.com/acme/demo", "email": "sales@acme.com"})
vendo("PATCH", f"/agents/{aid}/status", {"status": "ACTIVE"})

channel = vendo("POST", f"/agents/{aid}/connections", {
    "type": "WEBSITE_WIDGET", "website_url": "acme.com",
    "widget_config": {"teaser": True, "collectName": True, "collectEmail": True,
                      "texts": {"welcomeMessage": "Hi! I am Anna from Acme."}}})
print(vendo("GET", f"/agents/{aid}/connections/{channel['id']}/embed-code")["embed_code"])

Put an agent on the phone with an existing number

Uses a number you already own (numbers are bought in the app). Calls cost 25 credits per minute; the test call dials a real phone and needs dry_run → confirm.

curl
H=(-H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json")
API=https://api.vendo-ai.com/v1
AGENT_ID=your-agent-id   # an agent with a generated script

# 1. Numbers you already own (buying numbers is done in the app)
NUMBER_ID=$(curl -s $API/phone/numbers "${H[@]}" | jq -r '.data[0].id')

# 2. Optional: draft a call script for an objective (free, not saved)
curl -s -X POST $API/agents/$AGENT_ID/phone/generate-script "${H[@]}" -d '{"call_objective":"book_meeting"}'

# 3. Set up the Voice Call channel (up to ~1 min); accept_incoming answers calls too
curl -s -X POST $API/agents/$AGENT_ID/phone/setup "${H[@]}" -d "{
  \"phone_number_id\": \"$NUMBER_ID\", \"call_objective\": \"book_meeting\",
  \"voice_gender\": \"female\", \"accept_incoming\": true }"

# 4. Real test call to your own phone — preview, then confirm (25 credits/min, max 2 min)
curl -s -X POST $API/agents/$AGENT_ID/phone/test-call "${H[@]}" -d '{"to_number":"+447700900123","dry_run":true}'
curl -s -X POST $API/agents/$AGENT_ID/phone/test-call "${H[@]}" -d '{"to_number":"+447700900123","confirm":true}'

# 5. Call log with outcomes and recordings
curl -s "$API/agents/$AGENT_ID/phone/calls" "${H[@]}"
JavaScript
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data; // 202 (job still running) also lands here
}

const agentId = process.env.AGENT_ID;            // agent with a generated script
const numbers = await vendo("GET", "/phone/numbers");
if (!numbers.length) throw new Error("Buy a number in the app first");

await vendo("POST", `/agents/${agentId}/phone/setup`, {
  phone_number_id: numbers[0].id,
  call_objective: "book_meeting",
  voice_gender: "female",
  accept_incoming: true,
});

const preview = await vendo("POST", `/agents/${agentId}/phone/test-call`,
  { to_number: "+447700900123", dry_run: true });
console.log(preview);                              // show to a human first
await vendo("POST", `/agents/${agentId}/phone/test-call`,
  { to_number: "+447700900123", confirm: true });  // dials a real phone

console.log(await vendo("GET", `/agents/${agentId}/phone/calls`));
Python
import os, time, requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]   # 202 (job still running) also lands here

aid = os.environ["AGENT_ID"]                      # agent with a generated script
numbers = vendo("GET", "/phone/numbers")
assert numbers, "Buy a number in the app first"

vendo("POST", f"/agents/{aid}/phone/setup", {
    "phone_number_id": numbers[0]["id"], "call_objective": "book_meeting",
    "voice_gender": "female", "accept_incoming": True})

print(vendo("POST", f"/agents/{aid}/phone/test-call",
            {"to_number": "+447700900123", "dry_run": True}))   # preview
vendo("POST", f"/agents/{aid}/phone/test-call",
      {"to_number": "+447700900123", "confirm": True})            # real call
print(vendo("GET", f"/agents/{aid}/phone/calls"))

Run Vendo SDR: company + website → first contacts → outreach

The same flow as the New SDR wizard. The research reuses an existing company profile for the domain when there is one; otherwise it builds a hidden one (deep scan 15 credits + playbook 1 credit) that never counts toward your agent limit. Contacts cost 5 credits each; outreach starts only after confirm.

curl
H=(-H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json")
API=https://api.vendo-ai.com/v1

# 1. Company + website: preview cost/reuse, then start (deep scan 15 credits + playbook 1 credit
#    when a new company profile is built; the ICP research itself is free)
curl -s -X POST $API/sdr/research "${H[@]}" -d '{"company_name":"Brightsmile Software","website":"brightsmile.io","dry_run":true}'
AGENT_ID=$(curl -s -X POST $API/sdr/research "${H[@]}" \
  -d '{"company_name":"Brightsmile Software","website":"brightsmile.io","confirm":true}' | jq -r .data.agent_id)

# 2. Wait for the company profile, then for the research job
until [ "$(curl -s "$API/sdr/research?agent_id=$AGENT_ID" "${H[@]}" | jq -r .data.job.id)" != "null" ]; do sleep 10; done
JOB_ID=$(curl -s "$API/sdr/research?agent_id=$AGENT_ID" "${H[@]}" | jq -r .data.job.id)
until [[ "$(curl -s $API/sdr/research/$JOB_ID "${H[@]}" | jq -r .data.job.status)" =~ done|error ]]; do sleep 5; done
curl -s $API/sdr/research/$JOB_ID "${H[@]}" | jq .data.job.result.profile     # review the ICP

# 3. Create & start the search from the research (all 24 hunters, first buyers copied in)
SEARCH_ID=$(curl -s -X POST $API/sdr/searches "${H[@]}" \
  -d "{\"research_job_id\": \"$JOB_ID\", \"target_regions\": [\"GB\"]}" | jq -r .data.search.id)

# 4. Contacts that passed the ICP check (5 credits per usable contact)
curl -s "$API/sdr/people?search_id=$SEARCH_ID&group=fits&limit=50" "${H[@]}"

# 5. Messages, then launch outreach: preview, then confirm
curl -s -X PATCH $API/sdr/searches/$SEARCH_ID/outreach/content "${H[@]}" -d '{
  "email": {"mode": "ai", "prompt": "Short, specific, one question.", "tone": "friendly"},
  "linkedin": {"invite_enabled": true, "invite_mode": "ai",
               "conversation_rules": "Goal: a 15-minute demo. Offer https://cal.com/brightsmile/demo."}}'
LAUNCH='{"channels":["email","linkedin"],"email_sender_mode":"client_owned","client_email_provider":"google",
 "timezone":"Europe/London","work_start_hour":9,"work_end_hour":17,"exclude_weekends":true,"icp_segment":"fit"'
curl -s -X POST $API/sdr/searches/$SEARCH_ID/outreach/launch "${H[@]}" -d "$LAUNCH,\"dry_run\":true}"
curl -s -X POST $API/sdr/searches/$SEARCH_ID/outreach/launch "${H[@]}" -d "$LAUNCH,\"confirm\":true}"
JavaScript
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data; // 202 (job still running) also lands here
}

// 1. Company + website → company profile + ICP research
const company = { company_name: "Brightsmile Software", website: "brightsmile.io" };
console.log(await vendo("POST", "/sdr/research", { ...company, dry_run: true })); // cost & reuse
const started = await vendo("POST", "/sdr/research", { ...company, confirm: true });
const agentId = started.agent_id;

// 2. Poll: company profile build → research job → done
let st;
do { await sleep(10000); st = await vendo("GET", `/sdr/research?agent_id=${agentId}`); }
while (!st.job && st.setup?.status !== "error");
let job;
do { await sleep(5000); job = (await vendo("GET", `/sdr/research/${st.job.id}`)).job; }
while (!["done", "error"].includes(job.status));
console.log(job.result.profile);                 // offer, ICP, regions, hunter plan…

// 3. Create & start the search (defaults come from the research)
const { search } = await vendo("POST", "/sdr/searches", {
  research_job_id: job.id, target_regions: ["GB"],
});

// 4. Contacts that passed the ICP check
const fits = await vendo("GET", `/sdr/people?search_id=${search.id}&group=fits&limit=50`);
console.log(fits.items.length, "fit contacts so far");

// 5. Messages + launch (preview first)
await vendo("PATCH", `/sdr/searches/${search.id}/outreach/content`, {
  email: { mode: "ai", prompt: "Short, specific, one question.", tone: "friendly" },
  linkedin: { invite_enabled: true, invite_mode: "ai",
              conversation_rules: "Goal: a 15-minute demo. Offer https://cal.com/brightsmile/demo." },
});
const launch = {
  channels: ["email", "linkedin"], email_sender_mode: "client_owned", client_email_provider: "google",
  timezone: "Europe/London", work_start_hour: 9, work_end_hour: 17, exclude_weekends: true, icp_segment: "fit",
};
const report = await vendo("POST", `/sdr/searches/${search.id}/outreach/launch`, { ...launch, dry_run: true });
console.log(report);                              // blockers, ready contacts, delivery window
if (report.can_launch) await vendo("POST", `/sdr/searches/${search.id}/outreach/launch`, { ...launch, confirm: true });
Python
import os, time, requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]   # 202 (job still running) also lands here

company = {"company_name": "Brightsmile Software", "website": "brightsmile.io"}
print(vendo("POST", "/sdr/research", {**company, "dry_run": True}))     # cost & reuse
agent_id = vendo("POST", "/sdr/research", {**company, "confirm": True})["agent_id"]

st = {}
while not st.get("job"):                                               # company profile build
    time.sleep(10)
    st = vendo("GET", "/sdr/research", agent_id=agent_id)
    if (st.get("setup") or {}).get("status") == "error":
        raise RuntimeError(st["setup"])
job = st["job"]
while job["status"] not in ("done", "error"):                          # ICP research
    time.sleep(5)
    job = vendo("GET", f"/sdr/research/{job['id']}")["job"]
print(job["result"]["profile"])

search = vendo("POST", "/sdr/searches", {"research_job_id": job["id"], "target_regions": ["GB"]})["search"]

fits = vendo("GET", "/sdr/people", search_id=search["id"], group="fits", limit=50)
print(len(fits["items"]), "fit contacts so far")

vendo("PATCH", f"/sdr/searches/{search['id']}/outreach/content", {
    "email": {"mode": "ai", "prompt": "Short, specific, one question.", "tone": "friendly"},
    "linkedin": {"invite_enabled": True, "invite_mode": "ai",
                 "conversation_rules": "Goal: a 15-minute demo. Offer https://cal.com/brightsmile/demo."}})
launch = {"channels": ["email", "linkedin"], "email_sender_mode": "client_owned",
          "client_email_provider": "google", "timezone": "Europe/London",
          "work_start_hour": 9, "work_end_hour": 17, "exclude_weekends": True, "icp_segment": "fit"}
report = vendo("POST", f"/sdr/searches/{search['id']}/outreach/launch", {**launch, "dry_run": True})
print(report)
if report.get("can_launch"):
    vendo("POST", f"/sdr/searches/{search['id']}/outreach/launch", {**launch, "confirm": True})

Launch a classic multi-channel campaign

Preview an audience in the People database, save it, create a DRAFT campaign, check readiness with dry_run, then launch. Connect mailboxes with POST /v1/email/mailboxes and LinkedIn accounts with POST /v1/linkedin/accounts/connect (hosted login link) first.

curl
H=(-H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json")
API=https://api.vendo-ai.com/v1
FILTERS='{"countries":["United Kingdom"],"positions":["CFO","Finance Director"],"companySizes":["small","medium"],"mustHaveEmail":true}'

# 1. Free preview of the People database, then save the audience
curl -s -X POST $API/audiences/search "${H[@]}" -d "$FILTERS"
AUDIENCE_ID=$(curl -s -X POST $API/audiences "${H[@]}" \
  -d "{\"name\": \"UK finance leaders\", \"filters\": $FILTERS}" | jq -r .data.id)

# 2. Senders: your own mailboxes and LinkedIn accounts
curl -s $API/email/mailboxes "${H[@]}"
curl -s $API/linkedin/accounts "${H[@]}"

# 3. Create the campaign (DRAFT — nobody is contacted yet)
CAMPAIGN_ID=$(curl -s -X POST $API/campaigns "${H[@]}" -d "{
  \"name\": \"UK finance leaders — Q4\", \"audienceId\": \"$AUDIENCE_ID\", \"agentId\": \"$AGENT_ID\",
  \"settings\": {\"channels\": [\"email\", \"linkedin\"], \"dailyEmailLimit\": 80, \"dailyLinkedinLimit\": 40,
    \"timezone\": \"Europe/London\", \"workingHours\": [9, 18], \"excludeWeekends\": true,
    \"emailSenderMode\": \"client_owned\",
    \"emailContent\": {\"mode\": \"ai\", \"prompt\": \"Short, specific, one question.\", \"tone\": \"friendly\"}}}" | jq -r .data.id)

# 4. Preview what the first email will look like
curl -s -X POST $API/campaigns/preview/email "${H[@]}" -d "{\"campaign_id\": \"$CAMPAIGN_ID\"}"

# 5. Launch: readiness report, then confirm
curl -s -X POST $API/campaigns/$CAMPAIGN_ID/launch "${H[@]}" -d '{"dry_run":true}'
curl -s -X POST $API/campaigns/$CAMPAIGN_ID/launch "${H[@]}" -d '{"confirm":true}'

# 6. Watch it
curl -s $API/campaigns/$CAMPAIGN_ID/live-status "${H[@]}"
curl -s "$API/campaigns/$CAMPAIGN_ID/replies" "${H[@]}"
JavaScript
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data; // 202 (job still running) also lands here
}

const filters = { countries: ["United Kingdom"], positions: ["CFO", "Finance Director"],
                  companySizes: ["small", "medium"], mustHaveEmail: true };
console.log((await vendo("POST", "/audiences/search", filters)).stats);       // free preview
const audience = await vendo("POST", "/audiences", { name: "UK finance leaders", filters });

const campaign = await vendo("POST", "/campaigns", {
  name: "UK finance leaders — Q4",
  audienceId: audience.id,
  agentId: process.env.AGENT_ID,
  settings: {
    channels: ["email", "linkedin"], dailyEmailLimit: 80, dailyLinkedinLimit: 40,
    timezone: "Europe/London", workingHours: [9, 18], excludeWeekends: true,
    emailSenderMode: "client_owned",
    emailContent: { mode: "ai", prompt: "Short, specific, one question.", tone: "friendly" },
  },
});

const ready = await vendo("POST", `/campaigns/${campaign.id}/launch`, { dry_run: true });
console.log(ready.blockers, ready.recipient_count);
if (ready.ready) await vendo("POST", `/campaigns/${campaign.id}/launch`, { confirm: true });
console.log(await vendo("GET", `/campaigns/${campaign.id}/live-status`));
Python
import os, time, requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]   # 202 (job still running) also lands here

filters = {"countries": ["United Kingdom"], "positions": ["CFO", "Finance Director"],
           "companySizes": ["small", "medium"], "mustHaveEmail": True}
print(vendo("POST", "/audiences/search", filters)["stats"])                    # free preview
audience = vendo("POST", "/audiences", {"name": "UK finance leaders", "filters": filters})

campaign = vendo("POST", "/campaigns", {
    "name": "UK finance leaders — Q4", "audienceId": audience["id"],
    "agentId": os.environ["AGENT_ID"],
    "settings": {"channels": ["email", "linkedin"], "dailyEmailLimit": 80, "dailyLinkedinLimit": 40,
                 "timezone": "Europe/London", "workingHours": [9, 18], "excludeWeekends": True,
                 "emailSenderMode": "client_owned",
                 "emailContent": {"mode": "ai", "prompt": "Short, specific, one question.", "tone": "friendly"}}})

ready = vendo("POST", f"/campaigns/{campaign['id']}/launch", {"dry_run": True})
print(ready["blockers"], ready["recipient_count"])
if ready["ready"]:
    vendo("POST", f"/campaigns/{campaign['id']}/launch", {"confirm": True})
print(vendo("GET", f"/campaigns/{campaign['id']}/live-status"))

Work the CRM pipeline

Read pipelines and stages, filter leads, add and move leads, comment, reply on the lead's own channel (a real LinkedIn message or email) and schedule a follow-up.

curl
H=(-H "Authorization: Bearer $VENDO_API_KEY" -H "Content-Type: application/json")
API=https://api.vendo-ai.com/v1

# 1. Pipelines and the stages of the General pipeline
curl -s $API/crm/branches "${H[@]}"
curl -s "$API/crm/pipeline-stages?branch=general" "${H[@]}"

# 2. New leads from the website widget in the last 7 days
curl -s "$API/crm/leads?source=widget_chat&range=7d&limit=50" "${H[@]}"

# 3. Add a lead, move it to a stage, comment
LEAD_ID=$(curl -s -X POST $API/crm/leads "${H[@]}" \
  -d '{"first_name":"Tom","last_name":"Reyes","company_name":"BrightPay","email":"tom@brightpay.io"}' | jq -r .data.id)
curl -s -X PATCH $API/crm/leads/$LEAD_ID/stage "${H[@]}" -d "{\"stage_id\": \"$STAGE_ID\"}"
curl -s -X POST $API/crm/leads/$LEAD_ID/comments "${H[@]}" -d '{"body":"Asked for pricing for 12 seats."}'

# 4. Full conversation history, then reply on the lead's channel (real message)
curl -s $API/crm/leads/$LEAD_ID/conversation "${H[@]}"
curl -s -X POST $API/crm/leads/$LEAD_ID/reply "${H[@]}" -d '{"text":"Thanks Tom — sending the 12-seat quote today."}'

# 5. Book a follow-up in the CRM calendar
curl -s -X POST $API/crm/calendar/events "${H[@]}" \
  -d "{\"title\": \"Call Tom (BrightPay)\", \"start_at\": \"2026-10-28T10:00:00Z\", \"lead_id\": \"$LEAD_ID\"}"
JavaScript
const BASE = "https://api.vendo-ai.com/v1";
const KEY = process.env.VENDO_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function vendo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${json.code}: ${json.message}`);
  return json.data; // 202 (job still running) also lands here
}

const { stages } = await vendo("GET", "/crm/pipeline-stages?branch=general");
const newLeads = await vendo("GET", "/crm/leads?source=widget_chat&range=7d&limit=50");
console.log(newLeads);

const lead = await vendo("POST", "/crm/leads", {
  first_name: "Tom", last_name: "Reyes", company_name: "BrightPay", email: "tom@brightpay.io",
});
await vendo("PATCH", `/crm/leads/${lead.id}/stage`, { stage_id: stages[1].id });
await vendo("POST", `/crm/leads/${lead.id}/comments`, { body: "Asked for pricing for 12 seats." });
await vendo("POST", `/crm/calendar/events`, {
  title: "Call Tom (BrightPay)", start_at: "2026-10-28T10:00:00Z", lead_id: lead.id,
});
Python
import os, time, requests

BASE = "https://api.vendo-ai.com/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['VENDO_API_KEY']}"

def vendo(method, path, body=None, **params):
    r = S.request(method, BASE + path, json=body, params=params or None, timeout=260)
    if r.status_code >= 400:
        e = r.json()
        raise RuntimeError(f"{r.status_code} {e.get('code')}: {e.get('message')}")
    return r.json()["data"]   # 202 (job still running) also lands here

stages = vendo("GET", "/crm/pipeline-stages", branch="general")["stages"]
print(vendo("GET", "/crm/leads", source="widget_chat", range="7d", limit=50))

lead = vendo("POST", "/crm/leads", {"first_name": "Tom", "last_name": "Reyes",
                                    "company_name": "BrightPay", "email": "tom@brightpay.io"})
vendo("PATCH", f"/crm/leads/{lead['id']}/stage", {"stage_id": stages[1]["id"]})
vendo("POST", f"/crm/leads/{lead['id']}/comments", {"body": "Asked for pricing for 12 seats."})
vendo("POST", "/crm/calendar/events", {"title": "Call Tom (BrightPay)",
                                       "start_at": "2026-10-28T10:00:00Z", "lead_id": lead["id"]})

Connect from Claude via MCP

  1. Create a key in Settings → API & MCP (or a vnd_test_… key to explore safely).
  2. Add the server (Claude Code: claude mcp add --transport http vendo-ai https://api.vendo-ai.com/mcp --header "Authorization: Bearer vnd_live_…"; other clients: MCP setup).
  3. Ask in plain English. Claude picks tools such as list_agents, sdr_create_search or preview_launch_campaign and shows previews before any execute tool.

Example prompts:

  • Show my credit balance, my agents and which campaigns are active.
  • Create a sales agent "Anna" for acme.com, read the website, write a friendly script and give me the widget embed code.
  • Start Vendo SDR for Brightsmile Software (brightsmile.io) targeting the UK. Show me the ICP before creating the search.
  • Which SDR contacts in my "UK dental clinics" search are a fit and have an email? Export them to an audience.
  • Preview launching outreach for that search with email + LinkedIn, 9:00–17:00 London time, weekdays only. Do not launch yet.
  • List CRM leads from the website widget this week, move the ones that booked a meeting to "Qualified" and summarise their conversations.
  • Why did campaign "UK SaaS founders, Q4" stop sending? Check live status, mailboxes and LinkedIn accounts.

Zapier & Make

Zapier: add a Webhooks by Zapier › Custom Request step, set the method and URL (for example https://api.vendo-ai.com/v1/crm/leads), add the header Authorization: Bearer vnd_live_… and Content-Type: application/json, and put the JSON body in Data. Make: use the HTTP › Make a request module with the same URL, method and headers and enable Parse response. Because there are no webhooks yet, use a scheduled trigger and a list endpoint (for example new CRM leads with range=24h) to react to new events.

Coverage & limits

The API mirrors the web app: every product screen, agents and their channels, the meetings calendar, Vendo SDR, campaigns, mailboxes, LinkedIn, audiences, CRM, workspace settings, notifications, integrations, referral links and analytics, is available through REST and MCP.

Not available through the API (by design)

  • Registration, login and password reset
  • Payments, plan changes, credit top-ups, add-on purchases and the Stripe customer portal (billing is read-only)
  • Buying phone numbers (use numbers you already own via GET /v1/phone/numbers)
  • Referral payout requests (referral links and stats are available)
  • The in-app AI assistant chat
  • Account deletion, two-factor authentication settings and leaving a workspace

Billing is fully readable (balance, subscription, plan limits, pricing, transactions, invoices, credit usage) but never writable.

Needs a human step

Other limits

  • Rate limit 60 requests/minute per key; request bodies up to 50 MB (JSON up to 10 MB).
  • Job results (deep scan, script generation) are kept for 1 hour.
  • No webhooks or streaming yet, use the polling endpoints.

Changelog

2026-10-27, Round-6: full product parity

Every button of the web app now has an API route and an MCP tool. The release documents 362 REST endpoints (185 new, 89 fixed or changed) and 396 MCP tools (240 new, 79 fixed).

AreaEndpointsNewUpdatedMCP toolsNew / fixed tools
Agents99353210083
Meetings66088
Vendo SDR6927207555
Campaigns6652117767
Audiences261643126
CRM4814155334
Workspace & account303003434
Analytics & planner44044
Billing (read-only)1417148

New

  • Agents: Slide Presenter, Google Meet meetings and transcripts, Telegram chats and voice, Voice Call setup and test calls, WhatsApp accounts, knowledge files, the voices catalogue, conversations and recordings, jobs for long operations.
  • Meetings calendar; Vendo SDR wizard and ICP research, per-hunter configuration and stats, ICP QA, company details and exports; campaign emails, replies, AI threads, LinkedIn conversations, mailboxes and senders, phone campaigns, monitor and AI lead discovery, content accounts.
  • Custom audiences (upload, enrich, export); CRM pipelines (branches), documents, import, email from the lead card.
  • New scopes workspace:read / workspace:write: team and invitations, profile and notification preferences, in-app notifications, CRM integrations, referral program.

Fixes

  • Keys created in Settings → API & MCP are full-access and now receive every scope automatically, including crm:*, which older Settings keys were missing.
  • 79 MCP tools that returned errors or ignored arguments were fixed and now match their REST routes.
  • Security hardening: every id is now verified against your workspace; foreign ids answer 404.
  • Allow-listed request bodies on write routes (clear forbidden_field errors instead of silently ignored fields).

Breaking changes

2026-09-07, Round-4/5: re-audit, hunter catalogue and sandbox

  • Every route re-audited against the live code.
  • New GET /v1/sdr/hunters: the discoverable signal-hunter catalogue; enabled_sources documented on search create/update.
  • Test keys (vnd_test_) became a true read-only sandbox (403 sandbox_read_only on writes).

2026-07-22, Phase 7: UI parity

  • Audience Sets marketplace and LinkedIn account management.
  • CRM: workspace members, calendar, notifications, settings and the outbound Communications stream.
  • Vendo SDR: CSV contact export, per-person provenance, activity feed, outreach summary; more protected routes.

2026-07-06, CRM domain

  • REST endpoints and MCP tools for the built-in Vendo CRM; scopes crm:read / crm:write.

2026-06-15, Platform launch

  • Read-only billing; Vendo SDR signal pipeline; full agent lifecycle; the MCP server at api.vendo-ai.com/mcp; the destructive-action protocol and idempotency keys; the Settings → API & MCP tab; the core API.

What's next

  • Outbound webhooks (campaign launched, reply received, contact discovered).
  • A downloadable OpenAPI description.
  • Per-session credit ceilings for MCP clients.

FAQ

How do I get a Vendo AI API key?

Log in at app.vendo-ai.com, open Settings → API & MCP and create a key. The key (vnd_live_…) is shown once, so copy it immediately. Keys created there are full-access: they receive every scope, including scopes added in later releases.

What is the base URL and how do I authenticate?

REST: https://api.vendo-ai.com/v1. Send the key as "Authorization: Bearer vnd_live_…" or as "X-API-Key: vnd_live_…". MCP: https://api.vendo-ai.com/mcp (Streamable HTTP) with the same Bearer header.

How do I connect Vendo AI to Claude, ChatGPT or Cursor via MCP?

Add the server URL https://api.vendo-ai.com/mcp to your MCP client with the header Authorization: Bearer <your key>. In Claude Code run: claude mcp add --transport http vendo-ai https://api.vendo-ai.com/mcp --header "Authorization: Bearer vnd_live_…". Configs for Claude Desktop, Cursor, VS Code, Windsurf and the OpenAI and Anthropic APIs are on this page.

Can an AI assistant accidentally launch a campaign or delete data?

Destructive, paid or real-world actions use a two-step protocol. Over REST you send {"dry_run": true} to get a preview and then {"confirm": true} to execute; a request with neither returns 400 confirmation_required. Over MCP these actions are split into preview_* and execute tool pairs, so the model has to pick the execute tool explicitly.

Is there a sandbox or test mode?

Yes. Test keys (vnd_test_…) are read-only sandbox keys: GET requests, previews and dry_run calls work, and any real write is refused with 403 sandbox_read_only. Use a live key for real writes.

What happens with long operations such as a website deep scan or script generation?

They run as jobs. The request waits up to wait_seconds (default 200, maximum 240) and returns the result. If the job is still running you get HTTP 202 with a job_id; poll GET /v1/agents/{id}/jobs/{jobId} every 10-20 seconds. ICP research for Vendo SDR is polled with GET /v1/sdr/research/{id}.

Are there webhooks or streaming endpoints?

Not in this release. Everything the web app streams live (SDR activity, research progress, enrichment progress) is available through polling endpoints such as GET /v1/sdr/searches/{id}/outreach/activity-feed, GET /v1/sdr/research/{id} and GET /v1/audiences/custom/{id}/enrichment.

What are the rate limits?

By default 60 requests per minute per API key (sliding window). Responses carry X-RateLimit-Limit and X-RateLimit-Remaining; on 429 rate_limited a Retry-After header tells you how many seconds to wait.

Can the API change my plan, top up credits or buy phone numbers?

No. Billing is read-only by design: there is no billing write scope. Payments, plan changes, top-ups, buying phone numbers and referral payout requests stay in the web app. Registration and login, the in-app AI assistant chat, account deletion, 2FA and leaving a workspace are not available through the API either.

What happens if I use an id that belongs to another workspace?

Every id in a request is verified against the workspace of your API key. An id that belongs to another workspace answers 404 not_found, exactly like an id that does not exist, so the API never reveals whether another tenant's object exists.

Does the API cost extra?

API and MCP access are included in Vendo AI plans. Actions cost the same credits as in the web app (for example a deep website scan is 15 credits, each usable Vendo SDR contact is 5 credits, phone calls are billed per minute); reads are free. See the pricing page for plans.

More: Vendo AI for developers · AI sales agent · General FAQ · Pricing

Scroll to Top