n8n-nodes-instantly-dev
Version:
n8n community node for Instantly API v2
523 lines • 29.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccountOperations = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const generic_functions_1 = require("../../generic.functions");
const paginationHelpers_1 = require("../functions/paginationHelpers");
const resourceLocatorHelpers_1 = require("../functions/resourceLocatorHelpers");
/**
* Account operations handler
*/
class AccountOperations {
/**
* Get many accounts with pagination support
*/
static async getMany(context, itemIndex) {
const returnAll = context.getNodeParameter('returnAll', itemIndex, false);
const limit = context.getNodeParameter('limit', itemIndex, 50);
// Validate limit doesn't exceed 100
if (limit > 100) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Limit cannot exceed 100. Instantly API has a maximum limit of 100.', { itemIndex });
}
if (returnAll) {
// Get all accounts with pagination
const allAccounts = await (0, paginationHelpers_1.paginateInstantlyApi)(context, '/api/v2/accounts', 'accounts');
return { items: allAccounts };
}
else {
// Get single page with specified limit
const queryParams = { limit };
return await generic_functions_1.instantlyApiRequest.call(context, 'GET', '/api/v2/accounts', {}, queryParams);
}
}
/**
* Get single account by email
*/
static async get(context, itemIndex) {
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
return await generic_functions_1.instantlyApiRequest.call(context, 'GET', `/api/v2/accounts/${emailAccount}`);
}
/**
* Pause an account
*/
static async pause(context, itemIndex) {
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
const continueOnError = context.getNodeParameter('continueOnError', itemIndex, false);
try {
// Pause endpoint requires a simple POST without JSON content-type
const options = {
method: 'POST',
url: `https://api.instantly.ai/api/v2/accounts/${emailAccount}/pause`,
headers: {},
};
return await context.helpers.httpRequestWithAuthentication.call(context, 'instantlyApi', options);
}
catch (error) {
// Handle specific error cases for pause operation
if (error.response?.statusCode === 404) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Email account '${emailAccount}' not found. Please verify the email address is correct and the account exists in your Instantly workspace.`, { itemIndex });
}
else if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
// Account might already be paused or in an invalid state
if (continueOnError) {
// Return a success response with warning message
return {
success: true,
message: `Account '${emailAccount}' is already paused or cannot be paused in its current state.`,
warning: 'Operation skipped - account may already be in the target state',
email: emailAccount,
operation: 'pause',
skipped: true
};
}
else {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot pause account '${emailAccount}'. The account may already be paused or in a state that doesn't allow pausing. Please check the account status in your Instantly dashboard, or enable 'Continue on Error' to skip this error.`, { itemIndex });
}
}
else {
// Re-throw other errors with more context
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to pause account '${emailAccount}': ${error.message}`, { itemIndex });
}
}
}
/**
* Resume an account
*/
static async resume(context, itemIndex) {
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
const continueOnError = context.getNodeParameter('continueOnError', itemIndex, false);
try {
// Resume endpoint requires a simple POST without JSON content-type
const options = {
method: 'POST',
url: `https://api.instantly.ai/api/v2/accounts/${emailAccount}/resume`,
headers: {},
};
return await context.helpers.httpRequestWithAuthentication.call(context, 'instantlyApi', options);
}
catch (error) {
// Handle specific error cases for resume operation
if (error.response?.statusCode === 404) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Email account '${emailAccount}' not found. Please verify the email address is correct and the account exists in your Instantly workspace.`, { itemIndex });
}
else if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
// Account might already be resumed/active or in an invalid state
if (continueOnError) {
// Return a success response with warning message
return {
success: true,
message: `Account '${emailAccount}' is already active/resumed or cannot be resumed in its current state.`,
warning: 'Operation skipped - account may already be in the target state',
email: emailAccount,
operation: 'resume',
skipped: true
};
}
else {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot resume account '${emailAccount}'. The account may already be active/resumed or in a state that doesn't allow resuming. Please check the account status in your Instantly dashboard, or enable 'Continue on Error' to skip this error.`, { itemIndex });
}
}
else {
// Re-throw other errors with more context
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to resume account '${emailAccount}': ${error.message}`, { itemIndex });
}
}
}
/**
* Update an account
*/
static async update(context, itemIndex) {
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
const updateFields = context.getNodeParameter('updateFields', itemIndex);
// Build the update body with only provided fields using correct API field names
const updateBody = {};
// Basic account fields
if (updateFields.firstName !== undefined)
updateBody.first_name = updateFields.firstName;
if (updateFields.lastName !== undefined)
updateBody.last_name = updateFields.lastName;
if (updateFields.dailyLimit !== undefined)
updateBody.daily_limit = updateFields.dailyLimit;
if (updateFields.trackingDomainName !== undefined)
updateBody.tracking_domain_name = updateFields.trackingDomainName;
if (updateFields.trackingDomainStatus !== undefined)
updateBody.tracking_domain_status = updateFields.trackingDomainStatus;
if (updateFields.enableSlowRamp !== undefined)
updateBody.enable_slow_ramp = updateFields.enableSlowRamp;
if (updateFields.inboxPlacementTestLimit !== undefined)
updateBody.inbox_placement_test_limit = updateFields.inboxPlacementTestLimit;
if (updateFields.sendingGap !== undefined)
updateBody.sending_gap = updateFields.sendingGap;
if (updateFields.skipCnameCheck !== undefined)
updateBody.skip_cname_check = updateFields.skipCnameCheck;
if (updateFields.removeTrackingDomain !== undefined)
updateBody.remove_tracking_domain = updateFields.removeTrackingDomain;
// Warmup configuration
if (updateFields.warmup && Object.keys(updateFields.warmup).length > 0) {
const warmupConfig = {};
if (updateFields.warmup.limit !== undefined)
warmupConfig.limit = updateFields.warmup.limit;
if (updateFields.warmup.replyRate !== undefined)
warmupConfig.reply_rate = updateFields.warmup.replyRate;
if (updateFields.warmup.warmupCustomFtag !== undefined)
warmupConfig.warmup_custom_ftag = updateFields.warmup.warmupCustomFtag;
if (updateFields.warmup.increment !== undefined)
warmupConfig.increment = updateFields.warmup.increment;
updateBody.warmup = warmupConfig;
}
return await generic_functions_1.instantlyApiRequest.call(context, 'PATCH', `/api/v2/accounts/${emailAccount}`, updateBody);
}
/**
* Enable warmup for an account or all accounts
* Phase 1A: Critical Account Control - Enhanced with bulk operations
*
* IMPORTANT: Uses undocumented API v2 endpoints discovered through testing:
* - Endpoint: POST /api/v2/accounts/warmup/enable
* - Request format: {"emails": ["email@example.com"]} (NOT {"accounts": [...]})
* - Response: Returns async job tracking object with warmup_status: 1
*/
static async enableWarmup(context, itemIndex) {
const allEmails = context.getNodeParameter('allEmails', itemIndex, false);
try {
if (allEmails) {
// Bulk operation for all accounts with exclusions
const excludedEmails = context.getNodeParameter('excludedEmails', itemIndex, []);
// Get all accounts first
const allAccountsResponse = await generic_functions_1.instantlyApiRequest.call(context, 'GET', '/api/v2/accounts');
const allAccounts = allAccountsResponse.items || [];
// Filter out excluded emails
const targetEmails = allAccounts
.map((account) => account.email)
.filter((email) => !excludedEmails.includes(email));
if (targetEmails.length === 0) {
return {
success: true,
message: 'No accounts to enable warmup for (all accounts excluded)',
processed: 0,
excluded: excludedEmails.length
};
}
// Enable warmup for all target accounts
const results = [];
let successCount = 0;
let errorCount = 0;
for (const email of targetEmails) {
try {
// Use API v2 endpoint with CORRECT format: "emails" not "accounts"
const body = { emails: [email] };
const result = await generic_functions_1.instantlyApiRequest.call(context, 'POST', '/api/v2/accounts/warmup/enable', body);
results.push({ email, success: true, result });
successCount++;
}
catch (error) {
// Parse the actual error message from the API response
let errorMessage = error.message;
if (error.response?.body) {
if (typeof error.response.body === 'string') {
errorMessage = error.response.body;
}
else if (error.response.body.message) {
errorMessage = error.response.body.message;
}
else if (error.response.body.error) {
errorMessage = error.response.body.error;
}
}
// Handle specific error cases with more detailed messages
if (error.response?.statusCode === 400) {
if (errorMessage.toLowerCase().includes('already enabled') ||
errorMessage.toLowerCase().includes('warmup is already active') ||
errorMessage.toLowerCase().includes('warmup already enabled')) {
errorMessage = `Warmup is already enabled for account ${email}`;
}
else if (errorMessage.toLowerCase().includes('invalid') ||
errorMessage.toLowerCase().includes('bad request')) {
errorMessage = `Invalid account or warmup configuration for ${email}. Please verify the account exists and is properly configured.`;
}
else {
errorMessage = `Cannot enable warmup for ${email}: ${errorMessage}`;
}
}
else if (error.response?.statusCode === 404) {
errorMessage = `Account ${email} not found. Please verify the email address is correct.`;
}
else if (error.response?.statusCode === 422) {
errorMessage = `Account ${email} cannot have warmup enabled. The account may not be properly configured or may be in an invalid state.`;
}
else {
// Generic error handling for other status codes
errorMessage = `Failed to enable warmup for ${email}: ${errorMessage}`;
}
results.push({
email,
success: false,
error: errorMessage
});
errorCount++;
}
}
return {
success: true,
message: `Warmup enable operation completed for ${targetEmails.length} accounts`,
processed: targetEmails.length,
successful: successCount,
failed: errorCount,
excluded: excludedEmails.length,
results
};
}
else {
// Single account operation - use API v2 endpoint with CORRECT format
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
// Use CORRECT format: "emails" not "accounts" (discovered via CURL testing)
const body = { emails: [emailAccount] };
return await generic_functions_1.instantlyApiRequest.call(context, 'POST', '/api/v2/accounts/warmup/enable', body);
}
}
catch (error) {
// Enhanced error handling with specific messages
const emailAccount = allEmails ? 'accounts' : (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
// Parse the actual error message from the API response
let errorMessage = error.message;
if (error.response?.body) {
if (typeof error.response.body === 'string') {
errorMessage = error.response.body;
}
else if (error.response.body.message) {
errorMessage = error.response.body.message;
}
else if (error.response.body.error) {
errorMessage = error.response.body.error;
}
}
// Handle specific error cases for warmup enable operation
if (error.response?.statusCode === 404) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Email account '${emailAccount}' not found. Please verify the email address is correct and the account exists in your Instantly workspace.`, { itemIndex });
}
else if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
// Check for specific warmup status errors
if (errorMessage.toLowerCase().includes('already enabled') || errorMessage.toLowerCase().includes('warmup is already active')) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Warmup is already enabled for account '${emailAccount}'. No action needed.`, { itemIndex });
}
else {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot enable warmup for account '${emailAccount}': ${errorMessage}`, { itemIndex });
}
}
else {
// Re-throw other errors with the actual API error message
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to enable warmup for ${emailAccount}: ${errorMessage}`, { itemIndex });
}
}
}
/**
* Disable warmup for an account or all accounts
* Phase 1A: Critical Account Control - Enhanced with bulk operations
*
* IMPORTANT: Uses undocumented API v2 endpoints discovered through testing:
* - Endpoint: POST /api/v2/accounts/warmup/disable
* - Request format: {"emails": ["email@example.com"]} (NOT {"accounts": [...]})
* - Response: Returns async job tracking object with warmup_status: 0
*/
static async disableWarmup(context, itemIndex) {
const allEmails = context.getNodeParameter('allEmails', itemIndex, false);
try {
if (allEmails) {
// Bulk operation for all accounts with exclusions
const excludedEmails = context.getNodeParameter('excludedEmails', itemIndex, []);
// Get all accounts first
const allAccountsResponse = await generic_functions_1.instantlyApiRequest.call(context, 'GET', '/api/v2/accounts');
const allAccounts = allAccountsResponse.items || [];
// Filter out excluded emails
const targetEmails = allAccounts
.map((account) => account.email)
.filter((email) => !excludedEmails.includes(email));
if (targetEmails.length === 0) {
return {
success: true,
message: 'No accounts to disable warmup for (all accounts excluded)',
processed: 0,
excluded: excludedEmails.length
};
}
// Disable warmup for all target accounts
const results = [];
let successCount = 0;
let errorCount = 0;
for (const email of targetEmails) {
try {
// Use API v2 endpoint with CORRECT format: "emails" not "accounts"
const body = { emails: [email] };
const result = await generic_functions_1.instantlyApiRequest.call(context, 'POST', '/api/v2/accounts/warmup/disable', body);
results.push({ email, success: true, result });
successCount++;
}
catch (error) {
// Parse the actual error message from the API response
let errorMessage = error.message;
if (error.response?.body) {
if (typeof error.response.body === 'string') {
errorMessage = error.response.body;
}
else if (error.response.body.message) {
errorMessage = error.response.body.message;
}
else if (error.response.body.error) {
errorMessage = error.response.body.error;
}
}
// Handle specific error cases with more detailed messages
if (error.response?.statusCode === 400) {
if (errorMessage.toLowerCase().includes('already disabled') ||
errorMessage.toLowerCase().includes('warmup is already paused') ||
errorMessage.toLowerCase().includes('warmup already disabled')) {
errorMessage = `Warmup is already disabled for account ${email}`;
}
else if (errorMessage.toLowerCase().includes('invalid') ||
errorMessage.toLowerCase().includes('bad request')) {
errorMessage = `Invalid account or warmup configuration for ${email}. Please verify the account exists and is properly configured.`;
}
else {
errorMessage = `Cannot disable warmup for ${email}: ${errorMessage}`;
}
}
else if (error.response?.statusCode === 404) {
errorMessage = `Account ${email} not found. Please verify the email address is correct.`;
}
else if (error.response?.statusCode === 422) {
errorMessage = `Account ${email} cannot have warmup disabled. The account may not be properly configured or may be in an invalid state.`;
}
else {
// Generic error handling for other status codes
errorMessage = `Failed to disable warmup for ${email}: ${errorMessage}`;
}
results.push({
email,
success: false,
error: errorMessage
});
errorCount++;
}
}
return {
success: true,
message: `Warmup disable operation completed for ${targetEmails.length} accounts`,
processed: targetEmails.length,
successful: successCount,
failed: errorCount,
excluded: excludedEmails.length,
results
};
}
else {
// Single account operation - use API v2 endpoint with CORRECT format
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
// Use CORRECT format: "emails" not "accounts" (discovered via CURL testing)
const body = { emails: [emailAccount] };
return await generic_functions_1.instantlyApiRequest.call(context, 'POST', '/api/v2/accounts/warmup/disable', body);
}
}
catch (error) {
// Enhanced error handling with specific messages
const emailAccount = allEmails ? 'accounts' : (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
// Parse the actual error message from the API response
let errorMessage = error.message;
if (error.response?.body) {
if (typeof error.response.body === 'string') {
errorMessage = error.response.body;
}
else if (error.response.body.message) {
errorMessage = error.response.body.message;
}
else if (error.response.body.error) {
errorMessage = error.response.body.error;
}
}
// Handle specific error cases for warmup disable operation
if (error.response?.statusCode === 404) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Email account '${emailAccount}' not found. Please verify the email address is correct and the account exists in your Instantly workspace.`, { itemIndex });
}
else if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
// Check for specific warmup status errors
if (errorMessage.toLowerCase().includes('already disabled') || errorMessage.toLowerCase().includes('warmup is already paused')) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Warmup is already disabled for account '${emailAccount}'. No action needed.`, { itemIndex });
}
else {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot disable warmup for account '${emailAccount}': ${errorMessage}`, { itemIndex });
}
}
else {
// Re-throw other errors with the actual API error message
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to disable warmup for ${emailAccount}: ${errorMessage}`, { itemIndex });
}
}
}
/**
* Create a new account
* Phase 1A: Critical Account Management
*/
static async create(context, itemIndex) {
// Required fields
const email = context.getNodeParameter('email', itemIndex);
const password = context.getNodeParameter('password', itemIndex);
const smtpHost = context.getNodeParameter('smtpHost', itemIndex);
const smtpPort = context.getNodeParameter('smtpPort', itemIndex);
// Additional fields
const additionalFields = context.getNodeParameter('additionalFields', itemIndex, {});
const body = {
email,
password,
smtp_host: smtpHost,
smtp_port: smtpPort,
};
// Add optional fields if provided
if (additionalFields.firstName)
body.first_name = additionalFields.firstName;
if (additionalFields.lastName)
body.last_name = additionalFields.lastName;
if (additionalFields.signature)
body.signature = additionalFields.signature;
if (additionalFields.warmupEnabled !== undefined)
body.warmup_enabled = additionalFields.warmupEnabled;
try {
return await generic_functions_1.instantlyApiRequest.call(context, 'POST', '/api/v2/accounts', body);
}
catch (error) {
// Handle specific error cases for account creation
if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
const errorMessage = error.response?.body?.message || error.message;
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot create account '${email}'. ${errorMessage}. Please verify all account details are correct and the email is not already in use.`, { itemIndex });
}
else {
// Re-throw other errors with more context
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to create account '${email}': ${error.message}`, { itemIndex });
}
}
}
/**
* Delete an account
* Phase 1A: Critical Account Management
*/
static async deleteAccount(context, itemIndex) {
const emailAccount = (0, resourceLocatorHelpers_1.getEmailAccount)(context, itemIndex);
try {
return await generic_functions_1.instantlyApiRequest.call(context, 'DELETE', `/api/v2/accounts/${emailAccount}`);
}
catch (error) {
// Handle specific error cases for account deletion
if (error.response?.statusCode === 404) {
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Email account '${emailAccount}' not found. Please verify the email address is correct and the account exists in your Instantly workspace.`, { itemIndex });
}
else if (error.response?.statusCode === 400 || error.response?.statusCode === 422) {
// Account might be in use by active campaigns
const errorMessage = error.response?.body?.message || error.message;
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Cannot delete account '${emailAccount}'. ${errorMessage}. The account may be assigned to active campaigns. Please pause or remove the account from campaigns first.`, { itemIndex });
}
else {
// Re-throw other errors with more context
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Failed to delete account '${emailAccount}': ${error.message}`, { itemIndex });
}
}
}
}
exports.AccountOperations = AccountOperations;
//# sourceMappingURL=AccountOperations.js.map