Ketika Anda memverifikasi ribuan atau jutaan alamat email, menunggu secara sinkron untuk setiap hasil tidaklah praktis. Webhook verifikasi email menyediakan solusi elegan dengan memberi notifikasi kepada aplikasi Anda ketika tugas verifikasi selesai, menghilangkan kebutuhan untuk polling konstan dan memungkinkan alur kerja asinkron yang efisien. Panduan komprehensif ini mengeksplorasi segala yang perlu diketahui developer tentang mengimplementasikan webhook verifikasi email, dari konfigurasi dasar hingga pola lanjutan untuk menangani operasi verifikasi skala besar.
Memahami Webhook Verifikasi Email
Webhook adalah HTTP callback yang mengirimkan data ke aplikasi Anda ketika event tertentu terjadi. Dalam konteks verifikasi email, webhook memberi notifikasi kepada sistem Anda ketika pekerjaan verifikasi massal selesai, ketika verifikasi email individual selesai dalam mode async, atau ketika event signifikan lainnya terjadi selama proses verifikasi.
Mengapa Menggunakan Webhook untuk Verifikasi Email?
Pola request-response tradisional bekerja baik untuk verifikasi email tunggal, tetapi operasi massal menghadirkan tantangan. Memverifikasi 100.000 email mungkin memakan waktu berjam-jam, dan menjaga koneksi HTTP terbuka selama itu tidak layak. Polling untuk pembaruan status membuang resource dan menciptakan beban API yang tidak perlu.
Menghilangkan Overhead Polling
Tanpa webhook, Anda perlu berulang kali melakukan query ke API untuk memeriksa apakah pekerjaan massal telah selesai. Ini menciptakan trafik jaringan yang tidak perlu, mengonsumsi batas rate limit API, dan menambah kompleksitas pada aplikasi Anda. Webhook mendorong notifikasi kepada Anda tepat ketika dibutuhkan.
Pemrosesan Real-Time
Webhook memungkinkan tindakan segera ketika verifikasi selesai. Aplikasi Anda dapat memproses hasil, memperbarui database, dan memicu tindakan lanjutan tanpa penundaan yang diperkenalkan oleh interval polling.
Arsitektur yang Scalable
Arsitektur berbasis webhook dapat scale secara natural. Baik Anda memproses satu pekerjaan massal atau ratusan secara bersamaan, endpoint webhook Anda menerima notifikasi saat mereka tiba, dan Anda dapat memprosesnya secara asinkron menggunakan queue atau worker.
Efisiensi Resource
Alih-alih mempertahankan koneksi atau menjalankan loop polling, aplikasi Anda tetap idle sampai webhook tiba. Ini mengurangi biaya komputasi dan menyederhanakan persyaratan infrastruktur.
Event Webhook dalam Verifikasi Email
Layanan verifikasi email biasanya memicu webhook untuk beberapa jenis event:
Penyelesaian Pekerjaan Massal
Event webhook yang paling umum dipicu ketika pekerjaan verifikasi massal selesai diproses. Payload mencakup status pekerjaan, statistik ringkasan, dan informasi tentang mengunduh hasil.
Progress Pekerjaan Massal
Beberapa layanan mengirim webhook progress pada interval selama pemrosesan massal, memungkinkan Anda melacak kemajuan verifikasi dan memperkirakan waktu penyelesaian.
Kegagalan Pekerjaan Massal
Ketika pekerjaan massal mengalami error yang mencegah penyelesaian, webhook kegagalan memberikan detail tentang apa yang salah dan apakah hasil parsial tersedia.
Verifikasi Email Tunggal (Mode Async)
Untuk skenario verifikasi real-time volume tinggi, verifikasi email tunggal async mengirim hasil melalui webhook alih-alih menunggu respons sinkron.
Menyiapkan Endpoint Webhook
Mengimplementasikan webhook memerlukan pembuatan endpoint dalam aplikasi Anda yang dapat menerima dan memproses payload webhook.
Struktur Endpoint Dasar
Endpoint webhook hanyalah endpoint HTTP POST yang menerima payload JSON:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/email-verification', async (req, res) => {
const { event_type, job_id, status, data } = req.body;
console.log(`Received webhook: ${event_type} for job ${job_id}`);
// Process the webhook
try {
await handleWebhookEvent(req.body);
// Always respond quickly to acknowledge receipt
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
// Still acknowledge receipt to prevent retries
res.status(200).json({ received: true, error: error.message });
}
});
async function handleWebhookEvent(payload) {
switch (payload.event_type) {
case 'bulk.completed':
await handleBulkCompleted(payload);
break;
case 'bulk.failed':
await handleBulkFailed(payload);
break;
case 'bulk.progress':
await handleBulkProgress(payload);
break;
default:
console.log(`Unknown event type: ${payload.event_type}`);
}
}
Praktik Terbaik Respons Webhook
Layanan verifikasi email mengharapkan respons cepat dari endpoint webhook. Jika endpoint Anda memakan waktu terlalu lama untuk merespons, layanan mungkin menganggap pengiriman gagal dan mencoba lagi.
Respons Segera
Akui penerimaan webhook segera, lalu proses payload secara asinkron:
app.post('/webhooks/email-verification', async (req, res) => {
// Immediately acknowledge receipt
res.status(200).json({ received: true });
// Process asynchronously
setImmediate(async () => {
try {
await handleWebhookEvent(req.body);
} catch (error) {
console.error('Async webhook processing error:', error);
// Log for retry or manual processing
await logFailedWebhook(req.body, error);
}
});
});
Gunakan Message Queue untuk Pemrosesan Berat
Untuk sistem production, queue payload webhook untuk diproses oleh proses worker:
const Queue = require('bull');
const webhookQueue = new Queue('email-verification-webhooks');
app.post('/webhooks/email-verification', async (req, res) => {
// Queue the webhook for processing
await webhookQueue.add('process-webhook', req.body, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
}
});
res.status(200).json({ received: true });
});
// Worker process
webhookQueue.process('process-webhook', async (job) => {
const payload = job.data;
await handleWebhookEvent(payload);
});
Mengonfigurasi Webhook dengan API
Daftarkan endpoint webhook Anda dengan layanan verifikasi email:
async function registerWebhook(webhookUrl, events, secret) {
const response = await fetch('https://api.billionverify.com/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: webhookUrl,
events: events,
secret: secret
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(`Failed to register webhook: ${result.error}`);
}
console.log(`Webhook registered: ${result.webhook_id}`);
return result;
}
// Register for bulk job events
await registerWebhook(
'https://yourapp.com/webhooks/email-verification',
['bulk.completed', 'bulk.failed', 'bulk.progress'],
process.env.WEBHOOK_SECRET
);
Mengamankan Endpoint Webhook
Endpoint webhook dapat diakses publik, membuat keamanan menjadi esensial. Tanpa verifikasi yang tepat, penyerang dapat mengirim payload webhook palsu untuk memanipulasi aplikasi Anda.
Verifikasi Signature
Sebagian besar layanan verifikasi email menandatangani payload webhook menggunakan HMAC-SHA256 dengan shared secret. Verifikasi signature sebelum memproses:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/email-verification', async (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
const isValid = verifyWebhookSignature(
req.body,
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
console.warn('Invalid webhook signature received');
return res.status(401).json({ error: 'Invalid signature' });
}
// Signature valid, process webhook
await handleWebhookEvent(req.body);
res.status(200).json({ received: true });
});
Validasi Timestamp
Cegah replay attack dengan memvalidasi timestamp webhook:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const webhookTime = new Date(timestamp).getTime();
const currentTime = Date.now();
const difference = Math.abs(currentTime - webhookTime);
return difference <= toleranceSeconds * 1000;
}
app.post('/webhooks/email-verification', async (req, res) => {
const { timestamp } = req.body;
if (!isTimestampValid(timestamp)) {
console.warn('Webhook timestamp outside acceptable range');
return res.status(400).json({ error: 'Invalid timestamp' });
}
// Continue with signature verification and processing
});
IP Allowlisting
Untuk keamanan tambahan, batasi akses webhook ke alamat IP yang dikenal:
const allowedIPs = [
'203.0.113.0/24', // BillionVerify webhook servers
'198.51.100.0/24'
];
function isIPAllowed(clientIP) {
// Implement CIDR range checking
return allowedIPs.some(range => isIPInRange(clientIP, range));
}
app.post('/webhooks/email-verification', async (req, res) => {
const clientIP = req.ip || req.connection.remoteAddress;
if (!isIPAllowed(clientIP)) {
console.warn(`Webhook from unauthorized IP: ${clientIP}`);
return res.status(403).json({ error: 'Forbidden' });
}
// Continue with processing
});
Penanganan Idempotency
Webhook mungkin dikirim beberapa kali karena masalah jaringan atau retry. Implementasikan idempotency untuk menangani duplikat dengan aman:
const processedWebhooks = new Set(); // Use Redis in production
async function handleWebhookIdempotent(payload) {
const webhookId = payload.webhook_id || payload.event_id;
// Check if already processed
if (processedWebhooks.has(webhookId)) {
console.log(`Duplicate webhook ignored: ${webhookId}`);
return;
}
// Mark as processing
processedWebhooks.add(webhookId);
try {
await handleWebhookEvent(payload);
} catch (error) {
// Remove from processed set to allow retry
processedWebhooks.delete(webhookId);
throw error;
}
}
Untuk sistem production, gunakan Redis untuk idempotency terdistribusi:
const Redis = require('ioredis');
const redis = new Redis();
async function isWebhookProcessed(webhookId) {
const key = `webhook:processed:${webhookId}`;
const result = await redis.set(key, '1', 'NX', 'EX', 86400); // 24 hour expiry
return result === null; // Already exists
}
app.post('/webhooks/email-verification', async (req, res) => {
const webhookId = req.body.webhook_id;
if (await isWebhookProcessed(webhookId)) {
console.log(`Duplicate webhook: ${webhookId}`);
return res.status(200).json({ received: true, duplicate: true });
}
await handleWebhookEvent(req.body);
res.status(200).json({ received: true });
});
Memproses Payload Webhook
Event webhook yang berbeda memerlukan logika penanganan yang berbeda. Mari kita eksplorasi pola umum untuk memproses webhook verifikasi email.
Menangani Penyelesaian Pekerjaan Massal
Ketika pekerjaan verifikasi massal selesai, unduh dan proses hasilnya:
async function handleBulkCompleted(payload) {
const { job_id, status, summary, download_url } = payload;
console.log(`Bulk job ${job_id} completed with status: ${status}`);
console.log(`Summary: ${summary.valid} valid, ${summary.invalid} invalid`);
// Download results
const results = await downloadResults(download_url);
// Process results
await processVerificationResults(job_id, results);
// Update job status in database
await updateJobStatus(job_id, 'completed', summary);
// Notify relevant parties
await sendCompletionNotification(job_id, summary);
}
async function downloadResults(url) {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`
}
});
if (!response.ok) {
throw new Error(`Failed to download results: ${response.status}`);
}
return await response.json();
}
async function processVerificationResults(jobId, results) {
// Batch update contacts in database
const validEmails = results.filter(r => r.is_valid);
const invalidEmails = results.filter(r => !r.is_valid);
await db.transaction(async (trx) => {
// Update valid emails
for (const batch of chunkArray(validEmails, 1000)) {
await trx('contacts')
.whereIn('email', batch.map(r => r.email))
.update({
email_verified: true,
verification_date: new Date(),
verification_job_id: jobId
});
}
// Handle invalid emails
for (const batch of chunkArray(invalidEmails, 1000)) {
await trx('contacts')
.whereIn('email', batch.map(r => r.email))
.update({
email_verified: false,
email_invalid_reason: trx.raw('CASE email ' +
batch.map(r => `WHEN '${r.email}' THEN '${r.reason}'`).join(' ') +
' END'),
verification_date: new Date(),
verification_job_id: jobId
});
}
});
}
Menangani Kegagalan Pekerjaan Massal
Ketika pekerjaan gagal, tangkap informasi error dan tentukan apakah pemulihan dimungkinkan:
async function handleBulkFailed(payload) {
const { job_id, error_code, error_message, partial_results_available } = payload;
console.error(`Bulk job ${job_id} failed: ${error_message}`);
// Update job status
await updateJobStatus(job_id, 'failed', {
error_code,
error_message
});
// Try to retrieve partial results if available
if (partial_results_available) {
console.log('Attempting to retrieve partial results...');
try {
const partialResults = await downloadPartialResults(job_id);
await processVerificationResults(job_id, partialResults);
// Identify unprocessed emails for retry
const processedEmails = new Set(partialResults.map(r => r.email));
const originalEmails = await getOriginalJobEmails(job_id);
const unprocessedEmails = originalEmails.filter(e => !processedEmails.has(e));
if (unprocessedEmails.length > 0) {
// Schedule retry for unprocessed emails
await scheduleRetryJob(job_id, unprocessedEmails);
}
} catch (error) {
console.error('Failed to retrieve partial results:', error);
}
}
// Notify about failure
await sendFailureNotification(job_id, error_message);
}
async function scheduleRetryJob(originalJobId, emails) {
// Create new job for remaining emails
const response = await fetch('https://api.billionverify.com/v1/bulk/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
emails,
metadata: {
retry_of: originalJobId
}
})
});
const { job_id: newJobId } = await response.json();
console.log(`Scheduled retry job ${newJobId} for ${emails.length} emails`);
}
Menangani Pembaruan Progress
Webhook progress membantu melacak pekerjaan yang berjalan lama:
async function handleBulkProgress(payload) {
const { job_id, processed_count, total_count, estimated_completion } = payload;
const percentComplete = Math.round((processed_count / total_count) * 100);
console.log(`Job ${job_id}: ${percentComplete}% complete (${processed_count}/${total_count})`);
// Update progress in database
await updateJobProgress(job_id, {
processed_count,
total_count,
percent_complete: percentComplete,
estimated_completion: new Date(estimated_completion)
});
// Optionally notify users of progress
if (percentComplete % 25 === 0) {
await sendProgressNotification(job_id, percentComplete);
}
}
Pola Webhook Lanjutan
Sistem production mendapat manfaat dari pola lanjutan yang meningkatkan keandalan dan maintainability.
Dead Letter Queue untuk Webhook yang Gagal
Ketika pemrosesan webhook gagal berulang kali, pindahkan payload ke dead letter queue untuk review manual:
const webhookQueue = new Queue('email-verification-webhooks');
const deadLetterQueue = new Queue('webhook-dead-letters');
webhookQueue.process('process-webhook', async (job) => {
try {
await handleWebhookEvent(job.data);
} catch (error) {
// Check if this is the final retry
if (job.attemptsMade >= job.opts.attempts - 1) {
// Move to dead letter queue
await deadLetterQueue.add('failed-webhook', {
original_payload: job.data,
error: error.message,
failed_at: new Date().toISOString(),
attempts: job.attemptsMade + 1
});
}
throw error; // Re-throw to trigger retry
}
});
// Process dead letters manually or with alerts
deadLetterQueue.on('completed', async (job) => {
await sendAlert({
type: 'webhook_dead_letter',
job_id: job.data.original_payload.job_id,
error: job.data.error
});
});
Event Sourcing Webhook
Simpan semua event webhook untuk audit trail dan kemampuan replay:
async function handleWebhookWithEventSourcing(payload) {
// Store raw event
const eventId = await storeWebhookEvent(payload);
try {
// Process event
await handleWebhookEvent(payload);
// Mark as processed
await markEventProcessed(eventId);
} catch (error) {
// Mark as failed
await markEventFailed(eventId, error);
throw error;
}
}
async function storeWebhookEvent(payload) {
const result = await db('webhook_events').insert({
event_type: payload.event_type,
job_id: payload.job_id,
payload: JSON.stringify(payload),
received_at: new Date(),
status: 'pending'
});
return result[0];
}
// Replay failed events
async function replayFailedEvents() {
const failedEvents = await db('webhook_events')
.where('status', 'failed')
.where('retry_count', '<', 3);
for (const event of failedEvents) {
try {
await handleWebhookEvent(JSON.parse(event.payload));
await markEventProcessed(event.id);
} catch (error) {
await incrementRetryCount(event.id);
}
}
}
Routing Webhook Multi-Tenant
Untuk aplikasi SaaS, route webhook ke handler spesifik tenant:
async function handleMultiTenantWebhook(payload) {
const { tenant_id, event_type, data } = payload;
// Get tenant configuration
const tenant = await getTenantConfig(tenant_id);
if (!tenant) {
console.error(`Unknown tenant: ${tenant_id}`);
return;
}
// Route to tenant-specific handler
switch (event_type) {
case 'bulk.completed':
await handleTenantBulkCompleted(tenant, data);
break;
case 'bulk.failed':
await handleTenantBulkFailed(tenant, data);
break;
}
// Forward to tenant webhook if configured
if (tenant.webhook_url) {
await forwardToTenant(tenant.webhook_url, tenant.webhook_secret, payload);
}
}
async function forwardToTenant(url, secret, payload) {
const signature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature
},
body: JSON.stringify(payload)
});
}
Penanganan Error dan Keandalan
Implementasi webhook yang robust menangani kegagalan dengan baik dan memastikan tidak ada data yang hilang.
Strategi Retry
Implementasikan exponential backoff untuk kegagalan sementara:
async function processWebhookWithRetry(payload, maxRetries = 5) {
const delays = [1000, 5000, 30000, 120000, 300000]; // 1s, 5s, 30s, 2m, 5m
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
await handleWebhookEvent(payload);
return; // Success
} catch (error) {
const isRetryable = isRetryableError(error);
if (!isRetryable || attempt === maxRetries - 1) {
// Log to dead letter queue
await logFailedWebhook(payload, error, attempt + 1);
throw error;
}
console.log(`Retry ${attempt + 1}/${maxRetries} after ${delays[attempt]}ms`);
await sleep(delays[attempt]);
}
}
}
function isRetryableError(error) {
// Network errors, timeouts, and 5xx responses are retryable
const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
return retryableCodes.includes(error.code) ||
(error.status && error.status >= 500);
}
Pola Circuit Breaker
Cegah cascade failure ketika layanan downstream tidak tersedia:
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000;
this.state = 'CLOSED';
this.failures = 0;
this.lastFailure = null;
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailure > this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failures++;
this.lastFailure = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
console.warn('Circuit breaker opened due to failures');
}
}
}
const databaseCircuitBreaker = new CircuitBreaker();
async function handleBulkCompletedSafely(payload) {
await databaseCircuitBreaker.execute(async () => {
await processVerificationResults(payload.job_id, payload.results);
});
}
Monitoring dan Alerting
Lacak metrik kesehatan webhook:
const metrics = {
received: 0,
processed: 0,
failed: 0,
latency: []
};
app.post('/webhooks/email-verification', async (req, res) => {
const startTime = Date.now();
metrics.received++;
try {
await handleWebhookEvent(req.body);
metrics.processed++;
} catch (error) {
metrics.failed++;
throw error;
} finally {
metrics.latency.push(Date.now() - startTime);
// Keep only last 1000 measurements
if (metrics.latency.length > 1000) {
metrics.latency.shift();
}
}
res.status(200).json({ received: true });
});
// Expose metrics endpoint
app.get('/metrics/webhooks', (req, res) => {
const avgLatency = metrics.latency.reduce((a, b) => a + b, 0) / metrics.latency.length;
res.json({
received: metrics.received,
processed: metrics.processed,
failed: metrics.failed,
success_rate: (metrics.processed / metrics.received * 100).toFixed(2) + '%',
avg_latency_ms: Math.round(avgLatency)
});
});
// Alert on high failure rate
setInterval(() => {
const failureRate = metrics.failed / metrics.received;
if (failureRate > 0.1) { // More than 10% failures
sendAlert({
type: 'high_webhook_failure_rate',
failure_rate: failureRate,
total_received: metrics.received,
total_failed: metrics.failed
});
}
}, 60000);
Testing Implementasi Webhook
Testing menyeluruh memastikan webhook handler bekerja dengan benar di production.
Testing Lokal dengan ngrok
Gunakan ngrok untuk mengekspos endpoint lokal untuk testing webhook:
# Start your local server node server.js # In another terminal, expose it via ngrok ngrok http 3000
Daftarkan URL ngrok sebagai endpoint webhook Anda selama development.
Mock Payload Webhook
Buat fixture test untuk jenis event yang berbeda:
const mockPayloads = {
bulkCompleted: {
event_type: 'bulk.completed',
job_id: 'job_123456',
status: 'completed',
timestamp: new Date().toISOString(),
summary: {
total: 1000,
valid: 850,
invalid: 120,
risky: 30
},
download_url: 'https://api.billionverify.com/v1/bulk/download/job_123456'
},
bulkFailed: {
event_type: 'bulk.failed',
job_id: 'job_789012',
error_code: 'PROCESSING_ERROR',
error_message: 'Internal processing error',
partial_results_available: true
},
bulkProgress: {
event_type: 'bulk.progress',
job_id: 'job_345678',
processed_count: 5000,
total_count: 10000,
estimated_completion: new Date(Date.now() + 3600000).toISOString()
}
};
// Test endpoint
describe('Webhook Handler', () => {
it('should process bulk.completed event', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', generateSignature(mockPayloads.bulkCompleted))
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(200);
expect(response.body.received).toBe(true);
// Verify side effects
const job = await db('verification_jobs').where('job_id', 'job_123456').first();
expect(job.status).toBe('completed');
});
});
Integration Testing
Test alur webhook lengkap termasuk verifikasi signature:
describe('Webhook Security', () => {
it('should reject requests without signature', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(401);
});
it('should reject requests with invalid signature', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', 'invalid_signature')
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(401);
});
it('should accept requests with valid signature', async () => {
const signature = generateSignature(mockPayloads.bulkCompleted);
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', signature)
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(200);
});
});
Integrasi Webhook BillionVerify
BillionVerify menyediakan dukungan webhook komprehensif untuk event verifikasi email, memudahkan membangun alur kerja verifikasi asinkron.
Mengonfigurasi Webhook
Siapkan webhook melalui dashboard BillionVerify atau API:
// Register webhook via API
async function setupBillionVerifyWebhooks() {
const webhook = await registerWebhook(
'https://yourapp.com/webhooks/billionverify',
['bulk.completed', 'bulk.failed', 'bulk.progress'],
process.env.BILLIONVERIFY_WEBHOOK_SECRET
);
console.log('Webhook configured:', webhook);
}
Format Payload Webhook
Webhook BillionVerify mencakup informasi komprehensif tentang event verifikasi:
{
"event_type": "bulk.completed",
"webhook_id": "wh_abc123",
"job_id": "job_xyz789",
"timestamp": "2025-01-15T10:30:00Z",
"status": "completed",
"summary": {
"total": 10000,
"valid": 8500,
"invalid": 1200,
"risky": 300,
"disposable": 150,
"catch_all": 200
},
"processing_time_ms": 45000,
"download_url": "https://api.billionverify.com/v1/bulk/download/job_xyz789"
}
Contoh Integrasi Lengkap
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook endpoint for BillionVerify
app.post('/webhooks/billionverify', async (req, res) => {
// Verify signature
const signature = req.headers['x-billionverify-signature'];
const isValid = verifySignature(req.body, signature);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Acknowledge immediately
res.status(200).json({ received: true });
// Process asynchronously
processWebhookAsync(req.body);
});
async function processWebhookAsync(payload) {
try {
switch (payload.event_type) {
case 'bulk.completed':
await handleBulkCompleted(payload);
break;
case 'bulk.failed':
await handleBulkFailed(payload);
break;
case 'bulk.progress':
await handleBulkProgress(payload);
break;
}
} catch (error) {
console.error('Webhook processing error:', error);
await logFailedWebhook(payload, error);
}
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
Kesimpulan
Webhook verifikasi email mentransformasi cara aplikasi menangani verifikasi massal dengan memungkinkan pemrosesan asinkron yang efisien, scalable, dan andal. Dengan mengimplementasikan penanganan webhook yang tepat dengan tindakan keamanan, penanganan error, dan monitoring, Anda dapat membangun alur kerja verifikasi email yang robust yang scale sesuai kebutuhan aplikasi Anda.
Poin penting untuk mengimplementasikan webhook verifikasi email:
- Respons cepat terhadap request webhook dan proses payload secara asinkron
- Verifikasi signature untuk memastikan webhook berasal dari sumber yang sah
- Implementasikan idempotency untuk menangani pengiriman duplikat dengan aman
- Gunakan message queue untuk pemrosesan yang andal dalam skala besar
- Monitor kesehatan webhook dengan metrik dan alerting
Baik Anda memproses ribuan atau jutaan verifikasi email, webhook menyediakan fondasi untuk pemrosesan async yang efisien. Mulai implementasikan webhook hari ini dengan dukungan webhook komprehensif BillionVerify dan tingkatkan alur kerja verifikasi email Anda ke level berikutnya.