> ## Documentation Index
> Fetch the complete documentation index at: https://docs.botdog.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> Every Botdog MCP tool, its scope, and its input schema.

> Manually maintained from `apps/api/src/mcp/tools/`. When a tool is added, removed, or its schema changes, update this page in the same PR.

## Read tools — require `mcp.read`

### `list_campaigns`

List the authenticated team's campaigns. Optional status filter. Returns each campaign's id, name, status, leadCount, and createdAt.

```json theme={null}
{
  "status": "ACTIVE | PAUSED | ARCHIVED | COMPLETED | CREATED  (optional)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `get_campaign`

Fetch a single campaign by id.

```json theme={null}
{ "campaign_id": "string" }
```

### `get_campaign_statistics`

Heavy aggregation. Invitations sent/accepted, messages sent/replied, profile visits, acceptance/reply rates.

```json theme={null}
{ "campaign_id": "string" }
```

### `get_analytics`

Account-level analytics for a period.

```json theme={null}
{
  "period": "last-7-days | last-30-days | last-90-days (default last-7-days)",
  "user_id": "string (optional)"
}
```

### `get_analytics_daily`

Per-day breakdown for a date range (max 90 days).

```json theme={null}
{
  "start_date": "YYYY-MM-DD",
  "end_date": "YYYY-MM-DD",
  "user_id": "string (optional)",
  "campaign_id": "string (optional)"
}
```

### `get_analytics_by_user`

Per-team-member breakdown for a period.

```json theme={null}
{ "period": "last-7-days | last-30-days | last-90-days (default last-7-days)" }
```

### `list_users`

List the authenticated team's members.

```json theme={null}
{ "limit": "integer 1..200 (optional)" }
```

### `get_user`

Fetch one team member by id.

```json theme={null}
{ "user_id": "string" }
```

### `get_team`

Fetch the authenticated team's profile.

```json theme={null}
{}
```

### `get_leads_from_campaign`

List leads in a campaign (team-scoped). Optional filters by campaign status and reply state. Returns id, name, linkedinProfile, company, campaignStatus, and hasReplied. Paginated.

```json theme={null}
{
  "campaign_id": "string",
  "campaign_status": "CREATED | ACTIVE | PAUSED | ERROR | ARCHIVED | COMPLETED  (optional)",
  "has_replied": "boolean (optional)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `get_lead`

Look up a single lead by lead id or LinkedIn URL. Returns profile, company, campaign status, reply state, and last message.

```json theme={null}
{
  "lead_id": "string (one of lead_id or linkedin_url required)",
  "linkedin_url": "https://… (one of lead_id or linkedin_url required)"
}
```

### `find_leads_by_linkedin_url`

Find the lead(s) for one LinkedIn profile (team-scoped). The URL is normalized before matching, so any spelling works — `http`/`https`, with or without `www.`, a trailing slash, or tracking parameters; Sales Navigator and private profile URLs match too. The same person can hold one lead per campaign and one per list, so this returns a list — check `campaignId` and `campaignStatus` to pick the right one before stopping a sequence.

```json theme={null}
{
  "linkedin_url": "string (LinkedIn profile URL, any spelling)",
  "campaign_id": "string (optional, narrows to one campaign)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `list_replies`

Inbox view: leads who replied (team-scoped). Returns name, lastMessage preview, repliedAt, and unread state. Full message threads are not yet exposed.

```json theme={null}
{
  "unread_only": "boolean (optional)",
  "campaign_id": "string (optional)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `list_lists`

List the team's lead/audience lists. Returns id, name, isBlacklist, and leadCount. Paginated.

```json theme={null}
{
  "include_blacklist": "boolean (default false)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `get_list`

Fetch one lead/audience list by id. Returns name, isBlacklist, and leadCount.

```json theme={null}
{ "list_id": "string" }
```

### `get_leads_from_list`

List leads in a lead/audience list (team-scoped). Returns id, name, linkedinProfile, and company. Paginated.

```json theme={null}
{
  "list_id": "string",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `list_linkedin_accounts`

List the team's connected LinkedIn sender accounts. Returns each account's id, connection status, and owning user email.

```json theme={null}
{ "limit": "integer 1..200 (default 50)" }
```

### `get_account_limits`

Daily sending caps and remaining quota for today for one connected account (seat). Read-only.

```json theme={null}
{ "account_id": "integer" }
```

### `get_paused_activities`

List the activity types currently paused for a campaign (e.g. `SEND_INVITATION`). Empty when nothing is individually paused.

```json theme={null}
{ "campaign_id": "string" }
```

### `list_snippets`

List message snippets/templates visible to you: your personal snippets plus any shared with your team. Optional status filter. Paginated.

```json theme={null}
{
  "status": "ACTIVE | ARCHIVED  (optional)",
  "limit": "integer 1..100 (default 25)",
  "cursor": "opaque string (optional)"
}
```

### `list_webhooks`

List the team's webhooks. Returns id, name, endpoint, and status.

```json theme={null}
{}
```

### `get_webhook`

Fetch one webhook by id (team-scoped).

```json theme={null}
{ "webhook_id": "string" }
```

### `get_api_setup_guide`

Integration guide for the public REST API: base URL, `x-api-key` auth (key prefix `bd_live_`), a first-request snippet in the chosen language, and the list of Botdog operations (generated from this server's tools, each marked read or write). Unlike the other tools it does not mirror a single endpoint — it explains how to call the API from your own code.

```json theme={null}
{ "language": "curl | node | python (default node)" }
```

## Write tools — require `mcp.write`

### `add_leads_to_campaign`

Add 1–100 leads to a campaign. Adds REAL leads to a LIVE outreach campaign — only call after the user has confirmed both the campaign id and the lead list.

```json theme={null}
{
  "campaign_id": "string",
  "leads": [
    {
      "linkedinUrl": "https://… (one of linkedinUrl or email required)",
      "email": "name@example.com (one of linkedinUrl or email required)",
      "firstName": "string (optional)",
      "lastName": "string (optional)",
      "company": "string (optional)"
    }
  ]
}
```

### `pause_campaign`

Pause a running campaign (ACTIVE → PAUSED). Affects live outreach — the assistant confirms first. If the campaign was not ACTIVE, returns its current status unchanged.

```json theme={null}
{ "campaign_id": "string" }
```

### `resume_campaign`

Resume a paused campaign (PAUSED → ACTIVE) and re-engage outreach. Affects live outreach — the assistant confirms first. If the campaign was not PAUSED, returns its current status unchanged.

```json theme={null}
{ "campaign_id": "string" }
```

### `stop_lead_in_campaign`

Stop a single lead's progression in its campaign (cancels pending outreach for that lead). Identify the lead by `lead_id`, or by `campaign_id` + `linkedin_url` — the URL is normalized before matching, so any spelling works. If the person holds more than one lead in that campaign, the most recent one that isn't already stopped is used; call `find_leads_by_linkedin_url` first if you need to pick.

```json theme={null}
{
  "lead_id": "string (or supply campaign_id + linkedin_url)",
  "campaign_id": "string (with linkedin_url)",
  "linkedin_url": "https://… (with campaign_id)",
  "reason": "string, max 500 (optional, default 'Stopped via MCP')"
}
```

### `create_list`

Create a new empty lead/audience list. Set `is_blacklist` for an exclusion list. Returns the new list id.

```json theme={null}
{
  "name": "string (1..200)",
  "is_blacklist": "boolean (default false)"
}
```

### `add_leads_to_list`

Add up to 100 leads to an existing list. Each lead needs a `linkedinUrl`. Returns the count of newly created leads (duplicates are skipped).

```json theme={null}
{
  "list_id": "string",
  "leads": [
    {
      "linkedinUrl": "https://… (required)",
      "name": "string (optional)",
      "company": "string (optional)",
      "location": "string (optional)",
      "title": "string (optional)"
    }
  ]
}
```

### `delete_leads_from_list`

Delete leads from a list by `lead_ids` or `linkedin_urls`. Destructive — the assistant confirms first. Returns the number deleted.

```json theme={null}
{
  "list_id": "string",
  "lead_ids": ["string, 1..100 items (one of lead_ids or linkedin_urls required)"],
  "linkedin_urls": ["https://…, 1..100 items (one of lead_ids or linkedin_urls required)"]
}
```

### `create_webhook`

Create a webhook that posts event notifications to your endpoint. Returns the new webhook id.

```json theme={null}
{
  "name": "string (1..200)",
  "endpoint": "https://…",
  "event_types": ["string (at least one)"],
  "campaign_ids": ["string (optional — scope to specific campaigns)"]
}
```

### `update_webhook`

Update a webhook's name, endpoint, and/or event types.

```json theme={null}
{
  "webhook_id": "string",
  "name": "string 1..200 (optional)",
  "endpoint": "https://… (optional)",
  "event_types": ["string (optional)"]
}
```

### `delete_webhook`

Delete a webhook by id. Destructive — the assistant confirms first.

```json theme={null}
{ "webhook_id": "string" }
```

### `create_campaign`

Create a campaign (name + optional start/end dates). Creates the shell only — the message sequence is built, and the campaign launched, in the app.

```json theme={null}
{
  "name": "string (1..200)",
  "start_date": "ISO 8601 datetime (optional)",
  "end_date": "ISO 8601 datetime (optional)"
}
```

### `update_campaign`

Update a campaign's name and/or start/end dates. Does **not** change the message sequence or status (use `pause_campaign`/`resume_campaign` for status). Provide at least one field.

```json theme={null}
{
  "campaign_id": "string",
  "name": "string 1..200 (optional)",
  "start_date": "ISO 8601 datetime (optional)",
  "end_date": "ISO 8601 datetime (optional)"
}
```

### `pause_campaign_activities`

Pause specific activity types for a campaign (e.g. stop sending invitations while messages continue) without pausing the whole campaign. Affects live outreach. Reversible with `resume_campaign_activities`.

```json theme={null}
{
  "campaign_id": "string",
  "activity_types": [
    "PROFILE_VISIT | SEND_INVITATION | SEND_MESSAGE | SEND_INMAIL | SEND_COMPANY_PAGE_INVITATION | REACT_TO_POSTS (at least one)"
  ]
}
```

### `resume_campaign_activities`

Resume previously paused activity types for a campaign. Affects live outreach.

```json theme={null}
{
  "campaign_id": "string",
  "activity_types": [
    "PROFILE_VISIT | SEND_INVITATION | SEND_MESSAGE | SEND_INMAIL | SEND_COMPANY_PAGE_INVITATION | REACT_TO_POSTS (at least one)"
  ]
}
```

### `update_lead`

Update editable lead metadata (notes, custom fields, name, company). Does **not** change campaign/workflow status. Provide at least one field.

```json theme={null}
{
  "lead_id": "string",
  "name": "string, max 255 (optional)",
  "company": "string, max 255 (optional)",
  "status_note": "string, max 1000 (optional)",
  "custom_field_1": "string, max 1000 (optional)",
  "custom_field_2": "string, max 1000 (optional)",
  "custom_field_3": "string, max 1000 (optional)",
  "custom_attributes": { "key": "any JSON value (optional)" }
}
```

### `create_snippet`

Create a message snippet. `TEXT` snippets need `content`; `LLM` snippets need `prompt`.

```json theme={null}
{
  "name": "string (1..200)",
  "type": "TEXT | LLM (default TEXT)",
  "content": "string, max 10000 (for TEXT snippets)",
  "prompt": "string, max 10000 (for LLM snippets)",
  "shortcut": "string, max 50 (optional)"
}
```

### `update_snippet`

Update a snippet's name, content, prompt, shortcut, and/or status. Provide at least one field.

```json theme={null}
{
  "snippet_id": "string",
  "name": "string 1..200 (optional)",
  "content": "string, max 10000 (optional)",
  "prompt": "string, max 10000 (optional)",
  "shortcut": "string, max 50 (optional)",
  "status": "ACTIVE | ARCHIVED (optional)"
}
```

### `update_account_limits`

Update a connected account's daily caps, sending days, timezone, and auto-withdraw settings. Daily caps are **clamped to safe maximums** (you cannot set an unsafe value). Provide at least one field. Returns the resulting limits.

```json theme={null}
{
  "account_id": "integer",
  "max_daily_invites": "integer >= 0 (optional)",
  "max_daily_messages": "integer >= 0 (optional)",
  "max_daily_inmails": "integer >= 0 (optional)",
  "max_daily_profile_visits": "integer >= 0 (optional)",
  "max_daily_company_page_invitations": "integer >= 0 (optional)",
  "max_daily_react_to_posts": "integer >= 0 (optional)",
  "enable_auto_withdrawal": "boolean (optional)",
  "invite_withdrawal_duration": "integer 1..91 (optional)",
  "active_days_of_week": ["MONDAY … SUNDAY, at least one (optional)"],
  "timezone": "IANA timezone, e.g. America/New_York (optional)"
}
```
