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
| Name | Required | Description | Default |
|---|---|---|---|
| daily_limit | No | New daily sending limit | |
| Yes | Email address of the account to update | ||
| warmup_enabled | No | Enable/disable warmup |
Implementation Reference
- src/handlers/tool-executor.ts:663-705 (handler)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), }, ], }; }
- src/tools/account-tools.ts:67-109 (registration)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'] } },
- src/validation.ts:424-725 (schema)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');
- src/tools/index.ts:25-33 (registration)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,
- src/validation.ts:820-820 (helper)Maps the tool name 'update_account' to its validator function in the validation registry.'update_account': validateUpdateAccountData,