Skip to main content
Glama
bcharleson

Instantly MCP Server

move_leads_to_campaign_or_list

Transfer or duplicate leads between email campaigns and contact lists using specific IDs, search filters, or status criteria to organize outreach data.

Instructions

Move or copy leads between campaigns/lists.

Runs as background job for large operations.

Source selection (use one):

  • ids: Specific lead IDs to move

  • search + campaign/list_id: Filter leads by search term

  • filter + campaign/list_id: Filter by status

Destination (use one):

  • to_campaign_id: Target campaign

  • to_list_id: Target list

Set copy_leads=true to copy instead of move.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Main handler function that implements the tool logic by constructing a request body from MoveLeadsInput parameters and posting to the /leads/move API endpoint.
    async def move_leads_to_campaign_or_list(params: MoveLeadsInput) -> str:
        """
        Move or copy leads between campaigns/lists.
    
        Runs as background job for large operations.
    
        Source selection (use one):
        - ids: Specific lead IDs to move
        - search + campaign/list_id: Filter leads by search term
        - filter + campaign/list_id: Filter by status
    
        Destination (use one):
        - to_campaign_id: Target campaign
        - to_list_id: Target list
    
        Set copy_leads=true to copy instead of move.
        """
        client = get_client()
    
        body: dict[str, Any] = {}
    
        if params.to_campaign_id:
            body["to_campaign_id"] = params.to_campaign_id
        if params.to_list_id:
            body["to_list_id"] = params.to_list_id
        if params.ids:
            body["ids"] = params.ids
        if params.search:
            body["search"] = params.search
        if params.filter:
            body["filter"] = params.filter
        if params.campaign:
            body["campaign"] = params.campaign
        if params.list_id:
            body["list_id"] = params.list_id
        if params.in_campaign is not None:
            body["in_campaign"] = params.in_campaign
        if params.in_list is not None:
            body["in_list"] = params.in_list
        if params.queries:
            body["queries"] = params.queries
        if params.excluded_ids:
            body["excluded_ids"] = params.excluded_ids
        if params.contacts:
            body["contacts"] = params.contacts
        if params.check_duplicates_in_campaigns is not None:
            body["check_duplicates_in_campaigns"] = params.check_duplicates_in_campaigns
        if params.skip_leads_in_verification is not None:
            body["skip_leads_in_verification"] = params.skip_leads_in_verification
        if params.limit is not None:
            body["limit"] = params.limit
        if params.assigned_to:
            body["assigned_to"] = params.assigned_to
        if params.esp_code is not None:
            body["esp_code"] = params.esp_code
        if params.esg_code is not None:
            body["esg_code"] = params.esg_code
        if params.copy_leads is not None:
            body["copy_leads"] = params.copy_leads
        if params.check_duplicates is not None:
            body["check_duplicates"] = params.check_duplicates
    
        result = await client.post("/leads/move", json=body)
        return json.dumps(result, indent=2)
  • Pydantic schema defining the input parameters for the move_leads_to_campaign_or_list tool, including source selection, destination, and various options.
    class MoveLeadsInput(BaseModel):
        """
        Input for moving or copying leads between campaigns/lists.
        
        Runs as background job for large operations.
        """
        
        model_config = ConfigDict(str_strip_whitespace=True, extra="ignore")
        
        to_campaign_id: Optional[str] = Field(
            default=None,
            description="Destination (OR to_list_id)"
        )
        to_list_id: Optional[str] = Field(
            default=None,
            description="Destination (OR to_campaign_id)"
        )
        ids: Optional[list[str]] = Field(default=None, description="Lead IDs")
        search: Optional[str] = Field(default=None)
        filter: Optional[str] = Field(default=None, description="Contact status filter")
        campaign: Optional[str] = Field(default=None, description="Source campaign")
        list_id: Optional[str] = Field(default=None, description="Source list")
        in_campaign: Optional[bool] = Field(default=None)
        in_list: Optional[bool] = Field(default=None)
        queries: Optional[list[dict[str, Any]]] = Field(default=None)
        excluded_ids: Optional[list[str]] = Field(default=None)
        contacts: Optional[list[str]] = Field(default=None)
        check_duplicates_in_campaigns: Optional[bool] = Field(default=None)
        skip_leads_in_verification: Optional[bool] = Field(default=None)
        limit: Optional[int] = Field(default=None)
        assigned_to: Optional[str] = Field(default=None)
        esp_code: Optional[int] = Field(
            default=None,
            description="0=Queue, 1=Google, 2=MS, etc."
        )
        esg_code: Optional[int] = Field(
            default=None,
            description="0=Queue, 1=Barracuda, etc."
        )
        copy_leads: Optional[bool] = Field(default=None, description="Copy instead of move")
        check_duplicates: Optional[bool] = Field(default=None)
  • TOOL_ANNOTATIONS dictionary entry for 'move_leads_to_campaign_or_list' with destructiveHint: False, and the registration loop that applies annotations to all tools including this one.
    TOOL_ANNOTATIONS = {
        # Account tools
        "list_accounts": {"readOnlyHint": True},
        "get_account": {"readOnlyHint": True},
        "create_account": {"destructiveHint": False},
        "update_account": {"destructiveHint": False},
        "manage_account_state": {"destructiveHint": False},
        "delete_account": {"destructiveHint": True, "confirmationRequiredHint": True},
    
        # Campaign tools
        "create_campaign": {"destructiveHint": False},
        "list_campaigns": {"readOnlyHint": True},
        "get_campaign": {"readOnlyHint": True},
        "update_campaign": {"destructiveHint": False},
        "activate_campaign": {"destructiveHint": False},
        "pause_campaign": {"destructiveHint": False},
        "delete_campaign": {"destructiveHint": True, "confirmationRequiredHint": True},
        "search_campaigns_by_contact": {"readOnlyHint": True},
    
        # Lead tools
        "list_leads": {"readOnlyHint": True},
        "get_lead": {"readOnlyHint": True},
        "create_lead": {"destructiveHint": False},
        "update_lead": {"destructiveHint": False},
        "list_lead_lists": {"readOnlyHint": True},
        "create_lead_list": {"destructiveHint": False},
        "update_lead_list": {"destructiveHint": False},
        "get_verification_stats_for_lead_list": {"readOnlyHint": True},
        "add_leads_to_campaign_or_list_bulk": {"destructiveHint": False},
        "delete_lead": {"destructiveHint": True, "confirmationRequiredHint": True},
        "delete_lead_list": {"destructiveHint": True, "confirmationRequiredHint": True},
        "move_leads_to_campaign_or_list": {"destructiveHint": False},
    
        # Email tools
        "list_emails": {"readOnlyHint": True},
        "get_email": {"readOnlyHint": True},
        "reply_to_email": {"destructiveHint": True, "confirmationRequiredHint": True},
        "count_unread_emails": {"readOnlyHint": True},
        "verify_email": {"readOnlyHint": True},
        "mark_thread_as_read": {"destructiveHint": False},
    
        # Analytics tools
        "get_campaign_analytics": {"readOnlyHint": True},
        "get_daily_campaign_analytics": {"readOnlyHint": True},
        "get_warmup_analytics": {"readOnlyHint": True},
    
        # Background job tools
        "list_background_jobs": {"readOnlyHint": True},
        "get_background_job": {"readOnlyHint": True},
    }
    
    for tool_func in tools:
        tool_name = tool_func.__name__
        annotations = TOOL_ANNOTATIONS.get(tool_name, {})
        
        # Register tool with FastMCP
        mcp.tool(
            name=tool_name,
            annotations=annotations,
        )(tool_func)
    
    print(f"[Instantly MCP] ✅ Registered {len(tools)} tools", file=sys.stderr)
  • LEAD_TOOLS list that exports the move_leads_to_campaign_or_list function for inclusion in get_all_tools() used by server registration.
    LEAD_TOOLS = [
        list_leads,
        get_lead,
        create_lead,
        update_lead,
        list_lead_lists,
        create_lead_list,
        update_lead_list,
        get_verification_stats_for_lead_list,
        add_leads_to_campaign_or_list_bulk,
        delete_lead,
        delete_lead_list,
        move_leads_to_campaign_or_list,
    ]
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only provide destructiveHint=false, but the description adds valuable behavioral context: it discloses that operations run as background jobs for large operations, which implies asynchronous processing and potential delays. It also clarifies the move vs. copy behavior with copy_leads. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, followed by structured bullet-like sections for source selection, destination, and copy behavior. Every sentence adds essential information with zero waste, making it highly efficient and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (20+ parameters, background job behavior) and the presence of an output schema (which handles return values), the description is largely complete. It covers the main use cases and behavioral traits, though it doesn't detail all schema parameters like check_duplicates or limit, which could be helpful for advanced usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It effectively explains key parameters: source selection methods (ids, search, filter with campaign/list_id), destination options (to_campaign_id, to_list_id), and copy_leads. However, it doesn't cover all 20+ parameters in the schema, leaving some undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('move or copy leads between campaigns/lists'), identifies the resource ('leads'), and distinguishes from siblings like 'add_leads_to_campaign_or_list_bulk' by emphasizing movement/copying between existing campaigns/lists rather than bulk addition.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage rules: it specifies source selection options (ids, search+campaign/list_id, filter+campaign/list_id), destination options (to_campaign_id OR to_list_id), and when to use copy_leads=true. It also mentions it 'runs as background job for large operations,' indicating appropriate context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bcharleson/instantly-mcp-python'

If you have feedback or need assistance with the MCP directory API, please join our Discord server