ActiveCampaign
Email checker for ActiveCampaign. Verify contacts in your email marketing automation.
將 EmailVerify 與 ActiveCampaign 整合,驗證電子郵件地址、清理聯絡人清單並改善電子郵件可送達性。
整合方法
| 方法 | 最適合 | 複雜度 |
|---|---|---|
| Webhook 自動化 | 即時驗證 | 低 |
| API 整合 | 自訂工作流程 | 中 |
| 批量匯入 | 清單清理 | 低 |
方法 1:Webhook 自動化
建立在新增聯絡人時驗證電子郵件的自動化。
建立 Webhook 動作
- 進入 Automations → New Automation
- 選擇觸發器(例如「訂閱清單」)
- 新增動作 → Conditions and Workflow → Webhook
- 設定 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,
};
}
}