Skip to main content
Glama
bcharleson

Instantly MCP Server

update_account

Modify email account settings including daily sending limits and warmup configuration for improved email deliverability and campaign management.

Instructions

Update a sending account

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
daily_limitNoNew daily sending limit
emailYesEmail address of the account to update
warmup_enabledNoEnable/disable warmup

Implementation Reference

  • The core handler implementation for the 'update_account' tool. Builds a partial update object from input arguments (first_name, last_name, warmup config, daily_limit, sending_gap, tracking domain settings, etc.) and performs a PATCH request to the Instantly API endpoint `/accounts/{email}`.
    case 'update_account': {
      console.error('[Instantly MCP] 🔧 Executing update_account...');
    
      if (!args?.email) {
        throw new McpError(ErrorCode.InvalidParams, 'email is required');
      }
    
      // Build account update data with all provided parameters (excluding email from body)
      const updateData: any = {};
    
      // Add all optional parameters if provided
      if (args.first_name !== undefined) updateData.first_name = args.first_name;
      if (args.last_name !== undefined) updateData.last_name = args.last_name;
      if (args.warmup !== undefined) updateData.warmup = args.warmup;
      if (args.daily_limit !== undefined) updateData.daily_limit = args.daily_limit;
      if (args.tracking_domain_name !== undefined) updateData.tracking_domain_name = args.tracking_domain_name;
      if (args.tracking_domain_status !== undefined) updateData.tracking_domain_status = args.tracking_domain_status;
      if (args.enable_slow_ramp !== undefined) updateData.enable_slow_ramp = args.enable_slow_ramp;
      if (args.inbox_placement_test_limit !== undefined) updateData.inbox_placement_test_limit = args.inbox_placement_test_limit;
      if (args.sending_gap !== undefined) updateData.sending_gap = args.sending_gap;
      if (args.skip_cname_check !== undefined) updateData.skip_cname_check = args.skip_cname_check;
      if (args.remove_tracking_domain !== undefined) updateData.remove_tracking_domain = args.remove_tracking_domain;
    
      console.error(`[Instantly MCP] 📤 Updating account with data: ${JSON.stringify(updateData, null, 2)}`);
    
      const result = await makeInstantlyRequest(`/accounts/${encodeURIComponent(args.email)}`, {
        method: 'PATCH',
        body: updateData
      }, apiKey);
    
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              account: result,
              message: 'Account updated successfully'
            }, null, 2),
          },
        ],
      };
    }
  • Tool registration/definition including name, description, annotations, and detailed inputSchema used by MCP server.
    {
      name: 'update_account',
      title: 'Update Account',
      description: 'Update account settings (partial). Supports name, warmup, daily_limit, sending_gap, tracking_domain.',
      annotations: { destructiveHint: false },
      inputSchema: {
        type: 'object',
        properties: {
          email: { type: 'string', description: 'Account to update' },
          first_name: { type: 'string' },
          last_name: { type: 'string' },
          warmup: {
            type: 'object',
            properties: {
              limit: { type: 'number' },
              advanced: {
                type: 'object',
                properties: {
                  warm_ctd: { type: 'boolean' },
                  open_rate: { type: 'number' },
                  important_rate: { type: 'number' },
                  read_emulation: { type: 'boolean' },
                  spam_save_rate: { type: 'number' },
                  weekday_only: { type: 'boolean' }
                }
              },
              warmup_custom_ftag: { type: 'string' },
              increment: { type: 'string' },
              reply_rate: { type: 'number' }
            }
          },
          daily_limit: { type: 'number' },
          sending_gap: { type: 'number', description: 'Minutes between emails (0-1440)' },
          enable_slow_ramp: { type: 'boolean' },
          tracking_domain_name: { type: 'string' },
          tracking_domain_status: { type: 'string' },
          skip_cname_check: { type: 'boolean' },
          remove_tracking_domain: { type: 'boolean' },
          inbox_placement_test_limit: { type: 'number' }
        },
        required: ['email']
      }
    },
  • Zod schema (UpdateAccountSchema) and validator function (validateUpdateAccountData) for input validation of update_account tool arguments.
    export const UpdateAccountSchema = z.object({
      email: EmailSchema,
      first_name: z.string().optional(),
      last_name: z.string().optional(),
      warmup: z.object({
        limit: z.number().optional(),
        advanced: z.object({
          warm_ctd: z.boolean().optional(),
          open_rate: z.number().optional(),
          important_rate: z.number().optional(),
          read_emulation: z.boolean().optional(),
          spam_save_rate: z.number().optional(),
          weekday_only: z.boolean().optional()
        }).optional(),
        warmup_custom_ftag: z.string().optional(),
        increment: z.string().optional(),
        reply_rate: z.number().optional()
      }).optional(),
      daily_limit: z.number().int().min(1).max(1000).nullable().optional(),
      tracking_domain_name: z.string().nullable().optional(),
      tracking_domain_status: z.string().nullable().optional(),
      enable_slow_ramp: z.boolean().nullable().optional(),
      inbox_placement_test_limit: z.number().nullable().optional(),
      sending_gap: z.number().min(0).max(1440).optional(),
      skip_cname_check: z.boolean().optional(),
      remove_tracking_domain: z.boolean().optional()
    });
    
    /**
     * Lead creation validation schema - Updated to match Instantly.ai API v2 specification
     */
    export const CreateLeadSchema = z.object({
      // Core lead information
      campaign: z.string().optional(),
      email: z.string().email().optional(),
      first_name: z.string().optional(),
      last_name: z.string().optional(),
      company_name: z.string().optional(),
      phone: z.string().optional(),
      website: z.string().url().optional(),
      personalization: z.string().optional(),
    
      // Advanced parameters
      lt_interest_status: z.number().int().min(-3).max(4).optional(),
      pl_value_lead: z.string().optional(),
      list_id: z.string().optional(),
      assigned_to: z.string().optional(),
    
      // Skip conditions
      skip_if_in_workspace: z.boolean().optional(),
      skip_if_in_campaign: z.boolean().optional(),
      skip_if_in_list: z.boolean().optional(),
    
      // Verification and blocklist
      blocklist_id: z.string().optional(),
      verify_leads_for_lead_finder: z.boolean().optional(),
      verify_leads_on_import: z.boolean().optional(),
    
      // Custom variables
      custom_variables: z.record(z.string(), z.any()).optional()
    });
    
    /**
     * Update lead validation schema - Updated to match Instantly.ai API v2 specification
     */
    export const UpdateLeadSchema = z.object({
      lead_id: z.string().min(1, { message: 'Lead ID cannot be empty' }),
      personalization: z.string().optional(),
      website: z.string().url().optional(),
      last_name: z.string().optional(),
      first_name: z.string().optional(),
      company_name: z.string().optional(),
      phone: z.string().optional(),
      lt_interest_status: z.number().int().min(-3).max(4).optional(),
      pl_value_lead: z.string().optional(),
      assigned_to: z.string().optional(),
      custom_variables: z.record(z.string(), z.any()).optional()
    });
    
    /**
     * List leads validation schema
     */
    export const ListLeadsSchema = z.object({
      campaign_id: z.string().optional(),
      list_id: z.string().optional(),
      status: z.string().optional(),
      limit: z.number().int().min(1).max(100).optional(),
      starting_after: z.string().optional()
    });
    
    /**
     * Create lead list validation schema - Updated to match Instantly.ai API v2 specification
     */
    export const CreateLeadListSchema = z.object({
      name: z.string().min(1, { message: 'Lead list name cannot be empty' }),
      has_enrichment_task: z.boolean().optional(),
      owned_by: z.string().optional()
    });
    
    /**
     * List lead lists validation schema
     */
    export const ListLeadListsSchema = z.object({
      limit: z.number().int().min(1).max(100).optional(),
      starting_after: z.string().optional()
    });
    
    /**
     * Reply to email validation schema
     */
    export const ReplyToEmailSchema = z.object({
      reply_to_uuid: z.string()
        .min(1, { message: 'Reply to UUID cannot be empty' })
        .refine(
          (val) => val !== 'test-uuid',
          'reply_to_uuid must be a valid email ID. Use list_emails or get_email tools first to obtain a valid email UUID.'
        ),
      body: z.object({
        html: z.string().optional(),
        text: z.string().optional()
      }).refine(
        (val) => val.html || val.text,
        'Body must contain either html or text content'
      )
    });
    
    /**
     * List emails validation schema
     */
    export const ListEmailsSchema = z.object({
      campaign_id: z.string().optional(),
      account_id: z.string().optional(),
      limit: z.number().int().min(1).max(100).optional(),
      starting_after: z.string().optional()
    });
    
    /**
     * Get email validation schema
     */
    export const GetEmailSchema = z.object({
      email_id: z.string().min(1, { message: 'Email ID cannot be empty' })
    });
    
    /**
     * Validate campaign accounts validation schema
     */
    export const ValidateCampaignAccountsSchema = z.object({
      email_list: z.array(EmailSchema).optional()
    });
    
    /**
     * Get account details validation schema
     */
    export const GetAccountDetailsSchema = z.object({
      email: EmailSchema
    });
    
    // ============================================================================
    // VALIDATION HELPER FUNCTIONS
    // ============================================================================
    
    /**
     * Generic validation function that converts Zod errors to McpError
     */
    export function validateWithSchema<T>(
      schema: z.ZodSchema<T>,
      data: unknown,
      toolName: string
    ): T {
      try {
        return schema.parse(data);
      } catch (error) {
        if (error instanceof z.ZodError) {
          const errorMessages = error.issues.map((err: any) => {
            const path = err.path.length > 0 ? `${err.path.join('.')}: ` : '';
            return `${path}${err.message}`;
          });
          
          throw new McpError(
            ErrorCode.InvalidParams,
            `${toolName} validation failed: ${errorMessages.join('; ')}. Please check your input parameters and try again.`
          );
        }
        
        // Re-throw non-Zod errors
        throw error;
      }
    }
    
    /**
     * Validate campaign creation data with enhanced error messages and contextual guidance
     */
    export function validateCampaignData(args: unknown): z.infer<typeof CreateCampaignSchema> {
      try {
        return validateWithSchema(CreateCampaignSchema, args, 'create_campaign');
      } catch (error: any) {
        // Enhance error messages with contextual guidance
        if (error.message) {
          let enhancedMessage = error.message;
    
          // Add specific guidance for common issues
          if (error.message.includes('email_list') || error.message.includes('Placeholder')) {
            enhancedMessage += '\n\n💡 CRITICAL GUIDANCE: \n1️⃣ Call list_accounts first to see your available sender email addresses\n2️⃣ Only use verified, warmed-up accounts that show status=1 and warmup_status=1\n3️⃣ NEVER use placeholder emails like test@example.com, user@example.com, etc.\n4️⃣ To create ONE campaign with MULTIPLE emails, provide ALL emails in a SINGLE email_list array\n5️⃣ Example: email_list: ["real1@domain.com", "real2@domain.com"] creates ONE campaign with 2 senders';
          }
    
          if (error.message.includes('subject') || error.message.includes('Subject')) {
            enhancedMessage += '\n\n💡 GUIDANCE: Good subject lines are personal and specific. Examples:\n• "{{firstName}}, quick question about {{companyName}}" (48 chars)\n• "Helping {{companyName}} with [specific problem]" (adjust to <50)\n• "{{firstName}}, saw your recent [achievement/news]" (adjust to <50)\n\n⚠️ CRITICAL: Subject line MUST be under 50 characters. If your subject is too long, you MUST shorten it before retrying.';
          }
    
          if (error.message.includes('body') || error.message.includes('Body')) {
            enhancedMessage += '\n\n💡 GUIDANCE: Email body formatting tips:\n• Use \\n for line breaks - they will be automatically converted to <br /> tags for HTML email rendering\n• Double line breaks (\\n\\n) create new paragraphs\n• Personalize with {{firstName}}, {{lastName}}, {{companyName}}\n• Keep it conversational and specific\n• Example: "Hi {{firstName}},\\n\\nI noticed {{companyName}} recently [specific observation].\\n\\nBest regards,\\nYour Name"';
          }
    
          if (error.message.includes('daily_limit')) {
            enhancedMessage += '\n\n💡 GUIDANCE: Daily email limits for cold outreach:\n• Maximum 30 emails per day per account for compliance\n• Higher limits may trigger spam filters\n• Start with lower limits (10-20) for new accounts\n• Gradually increase as account reputation improves';
          }
    
          if (error.message.includes('track_opens') || error.message.includes('track_clicks')) {
            enhancedMessage += '\n\n💡 GUIDANCE: Email tracking considerations:\n• Tracking is disabled by default for better deliverability\n• Many email clients now block tracking pixels\n• Tracking can trigger spam filters and reduce trust\n• Enable only if analytics are absolutely necessary';
          }
    
          if (error.message.includes('name') && error.message.includes('Campaign')) {
            enhancedMessage += '\n\n💡 GUIDANCE: Use descriptive campaign names that help you identify the purpose. Examples:\n• "Q4 Product Launch - Tech Companies"\n• "Holiday Sales Outreach - Existing Customers"\n• "Partnership Inquiry - SaaS Companies"';
          }
    
          error.message = enhancedMessage;
        }
    
        throw error;
      }
    }
    
    /**
     * Validate campaign prerequisite check data with flexible validation
     */
    export function validateCampaignPrerequisiteData(args: unknown): z.infer<typeof CreateCampaignPrerequisiteSchema> {
      return validateWithSchema(CreateCampaignPrerequisiteSchema, args, 'create_campaign_prerequisite');
    }
    
    /**
     * Validate get campaign analytics parameters
     */
    export function validateGetCampaignAnalyticsData(args: unknown): z.infer<typeof GetCampaignAnalyticsSchema> {
      return validateWithSchema(GetCampaignAnalyticsSchema, args, 'get_campaign_analytics');
    }
    
    /**
     * Validate list accounts parameters
     */
    export function validateListAccountsData(args: unknown): z.infer<typeof ListAccountsSchema> {
      return validateWithSchema(ListAccountsSchema, args, 'list_accounts');
    }
    
    /**
     * Validate list campaigns parameters
     */
    export function validateListCampaignsData(args: unknown): z.infer<typeof ListCampaignsSchema> {
      return validateWithSchema(ListCampaignsSchema, args, 'list_campaigns');
    }
    
    /**
     * Validate warmup analytics parameters
     */
    export function validateWarmupAnalyticsData(args: unknown): z.infer<typeof GetWarmupAnalyticsSchema> {
      return validateWithSchema(GetWarmupAnalyticsSchema, args, 'get_warmup_analytics');
    }
    
    /**
     * Validate email verification parameters
     */
    export function validateEmailVerificationData(args: unknown): z.infer<typeof VerifyEmailSchema> {
      return validateWithSchema(VerifyEmailSchema, args, 'verify_email');
    }
    
    /**
     * Validate get campaign parameters
     */
    export function validateGetCampaignData(args: unknown): z.infer<typeof GetCampaignSchema> {
      return validateWithSchema(GetCampaignSchema, args, 'get_campaign');
    }
    
    /**
     * Validate update campaign parameters
     */
    export function validateUpdateCampaignData(args: unknown): z.infer<typeof UpdateCampaignSchema> {
      return validateWithSchema(UpdateCampaignSchema, args, 'update_campaign');
    }
    
    
    
    /**
     * Validate create account parameters
     */
    export function validateCreateAccountData(args: unknown): z.infer<typeof CreateAccountSchema> {
      return validateWithSchema(CreateAccountSchema, args, 'create_account');
    }
    
    /**
     * Validate update account parameters
     */
    export function validateUpdateAccountData(args: unknown): z.infer<typeof UpdateAccountSchema> {
      return validateWithSchema(UpdateAccountSchema, args, 'update_account');
  • Imports and registers accountTools (including update_account) into the main TOOLS_DEFINITION array used by MCP server.
    import { accountTools } from './account-tools.js';
    import { campaignTools } from './campaign-tools.js';
    import { leadTools } from './lead-tools.js';
    import { emailTools } from './email-tools.js';
    import { analyticsTools } from './analytics-tools.js';
    
    // Category mapping for lazy loading
    const CATEGORY_MAP: Record<string, any[]> = {
      accounts: accountTools,
  • Maps the tool name 'update_account' to its validator function in the validation registry.
    'update_account': validateUpdateAccountData,

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'

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