EmailVerify LogoEmailVerify

ActiveCampaign

Email checker for ActiveCampaign. Verify contacts in your email marketing automation.

將 EmailVerify 與 ActiveCampaign 整合,驗證電子郵件地址、清理聯絡人清單並改善電子郵件可送達性。

整合方法

方法最適合複雜度
Webhook 自動化即時驗證
API 整合自訂工作流程
批量匯入清單清理

方法 1:Webhook 自動化

建立在新增聯絡人時驗證電子郵件的自動化。

建立 Webhook 動作

  1. 進入 AutomationsNew Automation
  2. 選擇觸發器(例如「訂閱清單」)
  3. 新增動作 → Conditions and WorkflowWebhook
  4. 設定 webhook:
URL:https://api.emailverify.ai/v1/verify
方法:POST
Headers:
  Authorization:Bearer YOUR_API_KEY
  Content-Type:application/json
Body:
{
  "email": "%EMAIL%",
  "webhook_url": "https://yoursite.com/api/activecampaign/callback"
}

建立回調處理器

處理來自 EmailVerify 的驗證結果。

// pages/api/activecampaign/callback.js
import crypto from 'crypto';

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { email, status, result } = req.body;

  // 根據結果更新 ActiveCampaign 中的聯絡人
  const acApiUrl = process.env.ACTIVECAMPAIGN_API_URL;
  const acApiKey = process.env.ACTIVECAMPAIGN_API_KEY;

  try {
    // 按電子郵件尋找聯絡人
    const searchResponse = await fetch(
      `${acApiUrl}/api/3/contacts?email=${encodeURIComponent(email)}`,
      {
        headers: {
          'Api-Token': acApiKey,
        },
      }
    );

    const searchData = await searchResponse.json();
    const contact = searchData.contacts[0];

    if (!contact) {
      return res.status(404).json({ error: 'Contact not found' });
    }

    // 使用驗證狀態更新聯絡人
    const fieldUpdates = {
      contact: {
        fieldValues: [
          {
            field: process.env.AC_FIELD_EMAIL_STATUS,
            value: status,
          },
          {
            field: process.env.AC_FIELD_EMAIL_SCORE,
            value: result.score?.toString() || '',
          },
        ],
      },
    };

    await fetch(`${acApiUrl}/api/3/contacts/${contact.id}`, {
      method: 'PUT',
      headers: {
        'Api-Token': acApiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(fieldUpdates),
    });

    // 根據狀態新增標籤
    const tagName = status === 'valid' ? 'Email Verified' : 'Email Invalid';
    await addTagToContact(contact.id, tagName, acApiUrl, acApiKey);

    res.status(200).json({ success: true });
  } catch (error) {
    console.error('ActiveCampaign update error:', error);
    res.status(500).json({ error: 'Update failed' });
  }
}

async function addTagToContact(contactId, tagName, apiUrl, apiKey) {
  // 首先,尋找或建立標籤
  const tagResponse = await fetch(
    `${apiUrl}/api/3/tags?search=${encodeURIComponent(tagName)}`,
    {
      headers: { 'Api-Token': apiKey },
    }
  );

  const tagData = await tagResponse.json();
  let tagId = tagData.tags[0]?.id;

  if (!tagId) {
    // 如果不存在,建立標籤
    const createResponse = await fetch(`${apiUrl}/api/3/tags`, {
      method: 'POST',
      headers: {
        'Api-Token': apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        tag: { tag: tagName, tagType: 'contact' },
      }),
    });
    const createData = await createResponse.json();
    tagId = createData.tag.id;
  }

  // 將標籤新增到聯絡人
  await fetch(`${apiUrl}/api/3/contactTags`, {
    method: 'POST',
    headers: {
      'Api-Token': apiKey,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      contactTag: { contact: contactId, tag: tagId },
    }),
  });
}

方法 2:API 整合

使用兩個 API 建立自訂整合。

ActiveCampaign 服務

// services/activecampaign.js
import EmailVerify from '@emailverify/sdk';

class ActiveCampaignService {
  constructor() {
    this.apiUrl = process.env.ACTIVECAMPAIGN_API_URL;
    this.apiKey = process.env.ACTIVECAMPAIGN_API_KEY;
    this.bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);
  }

  async verifyContact(contactId) {
    // 取得聯絡人詳細資料
    const contact = await this.getContact(contactId);

    if (!contact?.email) {
      throw new Error('Contact has no email');
    }

    // 驗證電子郵件
    const result = await this.bv.verify({ email: contact.email });

    // 使用結果更新聯絡人
    await this.updateContactFields(contactId, {
      email_status: result.status,
      email_score: result.score,
      email_disposable: result.is_disposable ? 'Yes' : 'No',
      email_verified_at: new Date().toISOString(),
    });

    // 根據結果套用自動化
    if (result.status === 'invalid') {
      await this.addToAutomation(contactId, 'Invalid Email Cleanup');
    }

    return result;
  }

  async getContact(contactId) {
    const response = await fetch(
      `${this.apiUrl}/api/3/contacts/${contactId}`,
      {
        headers: { 'Api-Token': this.apiKey },
      }
    );
    const data = await response.json();
    return data.contact;
  }

  async updateContactFields(contactId, fields) {
    const fieldValues = await this.mapToFieldValues(fields);

    await fetch(`${this.apiUrl}/api/3/contacts/${contactId}`, {
      method: 'PUT',
      headers: {
        'Api-Token': this.apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        contact: { fieldValues },
      }),
    });
  }

  async mapToFieldValues(fields) {
    // 取得自訂欄位 ID
    const customFields = await this.getCustomFields();

    return Object.entries(fields).map(([key, value]) => ({
      field: customFields[key],
      value: String(value),
    }));
  }

  async getCustomFields() {
    const response = await fetch(`${this.apiUrl}/api/3/fields`, {
      headers: { 'Api-Token': this.apiKey },
    });
    const data = await response.json();

    return data.fields.reduce((acc, field) => {
      acc[field.perstag.toLowerCase()] = field.id;
      return acc;
    }, {});
  }

  async addToAutomation(contactId, automationName) {
    // 按名稱尋找自動化
    const response = await fetch(
      `${this.apiUrl}/api/3/automations?search=${encodeURIComponent(automationName)}`,
      {
        headers: { 'Api-Token': this.apiKey },
      }
    );
    const data = await response.json();
    const automation = data.automations[0];

    if (automation) {
      await fetch(`${this.apiUrl}/api/3/contactAutomations`, {
        method: 'POST',
        headers: {
          'Api-Token': this.apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          contactAutomation: {
            contact: contactId,
            automation: automation.id,
          },
        }),
      });
    }
  }

  async verifyList(listId) {
    // 取得清單中的所有聯絡人
    const contacts = await this.getListContacts(listId);
    const emails = contacts.map(c => c.email);

    // 批量驗證
    const job = await this.bv.verifyBulk(emails);

    // 等待完成
    let status;
    do {
      await new Promise(resolve => setTimeout(resolve, 5000));
      status = await this.bv.getBulkJobStatus(job.job_id);
    } while (status.status !== 'completed');

    // 取得結果並更新聯絡人
    const results = await this.bv.getBulkJobResults(job.job_id);

    for (const result of results.results) {
      const contact = contacts.find(c => c.email === result.email);
      if (contact) {
        await this.updateContactFields(contact.id, {
          email_status: result.status,
          email_score: result.score,
        });
      }
    }

    return {
      total: results.results.length,
      valid: results.results.filter(r => r.status === 'valid').length,
      invalid: results.results.filter(r => r.status === 'invalid').length,
    };
  }

  async getListContacts(listId) {
    const contacts = [];
    let offset = 0;
    const limit = 100;

    while (true) {
      const response = await fetch(
        `${this.apiUrl}/api/3/contacts?listid=${listId}&limit=${limit}&offset=${offset}`,
        {
          headers: { 'Api-Token': this.apiKey },
        }
      );
      const data = await response.json();

      if (data.contacts.length === 0) break;

      contacts.push(...data.contacts);
      offset += limit;
    }

    return contacts;
  }
}

export default new ActiveCampaignService();

使用範例

import activeCampaign from './services/activecampaign';

// 驗證單一聯絡人
const result = await activeCampaign.verifyContact('12345');
console.log(`Email status: ${result.status}`);

// 驗證整個清單
const listResults = await activeCampaign.verifyList('1');
console.log(`Verified ${listResults.total} contacts`);
console.log(`Valid: ${listResults.valid}, Invalid: ${listResults.invalid}`);

方法 3:表單整合

在提交前驗證 ActiveCampaign 表單上的電子郵件。

自訂表單處理器

<!-- 具有 EmailVerify 的 ActiveCampaign 表單 -->
<form id="ac-form" action="https://yoursite.activehosted.com/proc.php" method="POST">
  <input type="hidden" name="u" value="1" />
  <input type="hidden" name="f" value="1" />
  <input type="hidden" name="s" />
  <input type="hidden" name="c" value="0" />
  <input type="hidden" name="m" value="0" />
  <input type="hidden" name="act" value="sub" />
  <input type="hidden" name="v" value="2" />

  <div class="form-group">
    <label for="email">電子郵件地址</label>
    <input type="email" id="email" name="email" required />
    <span id="email-status"></span>
  </div>

  <div class="form-group">
    <label for="fullname">全名</label>
    <input type="text" id="fullname" name="fullname" />
  </div>

  <button type="submit" id="submit-btn">訂閱</button>
</form>

<script>
const form = document.getElementById('ac-form');
const emailInput = document.getElementById('email');
const emailStatus = document.getElementById('email-status');
const submitBtn = document.getElementById('submit-btn');

let emailValid = false;

emailInput.addEventListener('blur', async function() {
  const email = this.value;
  if (!email) return;

  emailStatus.textContent = 'Verifying...';
  emailStatus.className = 'verifying';
  submitBtn.disabled = true;

  try {
    const response = await fetch('/api/verify-email', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
    });

    const result = await response.json();

    if (result.status === 'valid') {
      emailStatus.textContent = '✓ Valid email';
      emailStatus.className = 'valid';
      emailValid = true;
    } else {
      emailStatus.textContent = '✗ Please enter a valid email';
      emailStatus.className = 'invalid';
      emailValid = false;
    }
  } catch (error) {
    // 在 API 錯誤時允許提交
    emailStatus.textContent = '';
    emailValid = true;
  }

  submitBtn.disabled = false;
});

form.addEventListener('submit', function(e) {
  if (!emailValid) {
    e.preventDefault();
    emailStatus.textContent = 'Please enter a valid email address';
    emailStatus.className = 'invalid';
  }
});
</script>

<style>
.form-group {
  margin-bottom: 1rem;
}

#email-status {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.875rem;
}

#email-status.verifying { color: #666; }
#email-status.valid { color: #28a745; }
#email-status.invalid { color: #dc3545; }

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>

自動化工作流程

訂閱時驗證

觸發器:聯絡人訂閱清單

動作:Webhook 到 EmailVerify

等待:直到 webhook 回應

若/否:檢查 email_status 欄位
  ├─ 若「有效」:繼續歡迎電子郵件
  └─ 若「無效」:新增到「無效電子郵件」清單,結束

定期清單清理

觸發器:每月執行一次

動作:Webhook 啟動批量驗證

等待:24 小時(用於批量工作完成)

目標:批量驗證完成

動作:透過 webhook 回調處理結果

重新參與行銷活動

觸發器:聯絡人 90 天內未開啟電子郵件

動作:驗證電子郵件地址

若/否:電子郵件狀態
  ├─ 若「有效」:發送重新參與電子郵件
  └─ 若「無效」:取消訂閱聯絡人

自訂欄位設定

在 ActiveCampaign 中建立這些自訂欄位:

欄位名稱類型個人化標籤
電子郵件狀態文字%EMAIL_STATUS%
電子郵件分數數字%EMAIL_SCORE%
電子郵件一次性下拉列表(是/否)%EMAIL_DISPOSABLE%
電子郵件驗證時間日期%EMAIL_VERIFIED_AT%

透過 API 建立欄位

async function createCustomFields(apiUrl, apiKey) {
  const fields = [
    { title: 'Email Status', type: 'text', perstag: 'EMAIL_STATUS' },
    { title: 'Email Score', type: 'text', perstag: 'EMAIL_SCORE' },
    { title: 'Email Disposable', type: 'dropdown', perstag: 'EMAIL_DISPOSABLE', options: ['Yes', 'No'] },
    { title: 'Email Verified At', type: 'date', perstag: 'EMAIL_VERIFIED_AT' },
  ];

  for (const field of fields) {
    await fetch(`${apiUrl}/api/3/fields`, {
      method: 'POST',
      headers: {
        'Api-Token': apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ field }),
    });
  }
}

分段

根據電子郵件驗證狀態建立區隔。

有效電子郵件區隔

條件:
- 聯絡人欄位「電子郵件狀態」為「有效」
- 聯絡人欄位「電子郵件分數」大於 0.7

無效電子郵件區隔

條件:
- 聯絡人欄位「電子郵件狀態」為「無效」
OR
- 聯絡人欄位「電子郵件狀態」為「未知」

高風險電子郵件區隔

條件:
- 聯絡人欄位「電子郵件一次性」為「是」
OR
- 聯絡人欄位「電子郵件分數」小於 0.5

最佳實務

1. 行銷活動前驗證

在主要行銷活動前始終驗證清單:

async function preCampaignVerification(listId, campaignId) {
  const service = new ActiveCampaignService();

  // 驗證清單
  const results = await service.verifyList(listId);

  // 僅在無效率可接受時繼續
  const invalidRate = results.invalid / results.total;

  if (invalidRate > 0.05) { // 超過 5% 無效
    console.warn(`High invalid rate: ${(invalidRate * 100).toFixed(1)}%`);
    console.warn('Consider cleaning list before sending');
    return false;
  }

  return true;
}

2. 處理速率限制

async function verifyWithRateLimit(contacts) {
  const batchSize = 100;
  const delayMs = 1000;

  for (let i = 0; i < contacts.length; i += batchSize) {
    const batch = contacts.slice(i, i + batchSize);

    await Promise.all(
      batch.map(contact => verifyContact(contact.id))
    );

    if (i + batchSize < contacts.length) {
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }
}

3. 錯誤處理

async function safeVerify(email) {
  try {
    const result = await bv.verify({ email });
    return result;
  } catch (error) {
    console.error(`Verification failed for ${email}:`, error);

    // 傳回未知狀態以允許進一步處理
    return {
      email,
      status: 'unknown',
      error: error.message,
    };
  }
}

相關資源

On this page