Integration Documentation
Status: Complete
Version: 5.0
Last Updated: 2024
Purpose: Comprehensive integration guide for connecting NudgeCampaign with external systems
Table of Contents
- Integration Overview
- REST API Documentation
- Webhook System
- Third-Party Integrations
- Authentication & Security
- SDK Documentation
- Data Synchronization
- Integration Patterns
- Testing & Debugging
- Migration Guides
Integration Overview
NudgeCampaign provides extensive integration capabilities to connect with your existing technology stack, enabling seamless data flow and automation across your marketing ecosystem.
Integration Architecture
Integration Capabilities
Core Features
- RESTful API with comprehensive endpoints
- Real-time webhook notifications
- Batch data import/export
- OAuth 2.0 authentication
- Rate limiting and throttling
- Multi-tenant data isolation
- Async processing for large operations
- Comprehensive error handling
Supported Protocols
- REST/HTTP(S)
- WebSocket for real-time updates
- GraphQL (beta)
- SOAP (legacy support)
- SFTP for bulk transfers
- Email (SMTP/IMAP)
Integration Types
1. Native Integrations
Pre-built connectors maintained by NudgeCampaign:
- Salesforce CRM
- HubSpot
- Shopify
- WooCommerce
- Stripe
- Zapier
- Microsoft Dynamics
- Google Analytics
2. Custom Integrations
Build your own integrations using:
- REST API
- Webhooks
- SDK libraries
- Custom connectors
3. Middleware Platforms
Connect through integration platforms:
- Zapier (1000+ apps)
- Make (formerly Integromat)
- n8n (self-hosted)
- Tray.io
- Workato
REST API Documentation
API Overview
Base URL: https://api.nudgecampaign.com/v1
All API requests must include authentication headers and follow REST conventions.
Authentication
API Key Authentication
curl -X GET https://api.nudgecampaign.com/v1/contacts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Organization-ID: YOUR_ORG_ID"
OAuth 2.0 Flow
// OAuth 2.0 Authorization Code Flow
const authUrl = 'https://auth.nudgecampaign.com/oauth/authorize';
const params = {
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'YOUR_REDIRECT_URI',
response_type: 'code',
scope: 'contacts:read campaigns:write',
state: 'RANDOM_STATE_STRING'
};
// Redirect user to authorization URL
window.location.href = `${authUrl}?${new URLSearchParams(params)}`;
// Exchange code for token
const tokenResponse = await fetch('https://auth.nudgecampaign.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
grant_type: 'authorization_code',
code: authorizationCode,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri
})
});
Core Endpoints
Contacts API
List Contacts
GET /v1/contacts
Query Parameters:
page(integer): Page number (default: 1)limit(integer): Items per page (max: 100)sort(string): Sort field and directionfilter(string): Filter expressionfields(string): Comma-separated field list
Response:
{
"data": [
{
"id": "cnt_abc123",
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"status": "subscribed",
"tags": ["customer", "vip"],
"customFields": {
"company": "Acme Corp",
"lifetime_value": 5000
},
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-02-20T14:45:00Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 1500,
"pages": 75
}
}
Create Contact
POST /v1/contacts
Request Body:
{
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"tags": ["prospect"],
"customFields": {
"source": "website",
"interest": "email-marketing"
},
"lists": ["lst_newsletter", "lst_prospects"]
}
Response:
{
"data": {
"id": "cnt_xyz789",
"email": "jane@example.com",
"status": "pending",
"confirmationUrl": "https://app.nudgecampaign.com/confirm/xyz789"
}
}
Update Contact
PATCH /v1/contacts/{id}
Request Body:
{
"firstName": "Jane",
"lastName": "Johnson",
"customFields": {
"company": "New Company"
},
"tags": {
"add": ["customer"],
"remove": ["prospect"]
}
}
Delete Contact
DELETE /v1/contacts/{id}
Bulk Operations
POST /v1/contacts/bulk
Request Body:
{
"operation": "upsert",
"contacts": [
{
"email": "contact1@example.com",
"firstName": "Contact",
"lastName": "One"
},
{
"email": "contact2@example.com",
"firstName": "Contact",
"lastName": "Two"
}
],
"options": {
"updateExisting": true,
"skipValidation": false
}
}
Campaigns API
List Campaigns
GET /v1/campaigns
Query Parameters:
status(string): draft, scheduled, sending, sent, pausedtype(string): regular, automated, ab_testfrom_date(string): ISO 8601 dateto_date(string): ISO 8601 date
Create Campaign
POST /v1/campaigns
Request Body:
{
"name": "Summer Sale Campaign",
"subject": "Exclusive Summer Deals Inside! βοΈ",
"preheader": "Save up to 50% on selected items",
"from": {
"email": "sales@example.com",
"name": "Sales Team"
},
"replyTo": "support@example.com",
"lists": ["lst_customers"],
"segments": ["seg_high_value"],
"content": {
"html": "<html>...</html>",
"text": "Plain text version..."
},
"schedule": {
"sendAt": "2024-06-01T10:00:00Z",
"timezone": "America/New_York"
}
}
Send Test Email
POST /v1/campaigns/{id}/test
Request Body:
{
"recipients": [
"test1@example.com",
"test2@example.com"
],
"personalization": {
"firstName": "Test",
"company": "Test Company"
}
}
Campaign Analytics
GET /v1/campaigns/{id}/analytics
Response:
{
"data": {
"sent": 10000,
"delivered": 9850,
"bounced": 150,
"opened": 3500,
"clicked": 850,
"unsubscribed": 25,
"complained": 5,
"metrics": {
"deliveryRate": 98.5,
"openRate": 35.5,
"clickRate": 8.6,
"clickToOpenRate": 24.3,
"unsubscribeRate": 0.25,
"complaintRate": 0.05
},
"engagement": {
"devices": {
"desktop": 45,
"mobile": 50,
"tablet": 5
},
"clients": {
"gmail": 30,
"outlook": 25,
"apple": 20,
"other": 25
}
},
"links": [
{
"url": "https://example.com/product",
"clicks": 450,
"uniqueClicks": 380
}
]
}
}
Lists API
Create List
POST /v1/lists
Request Body:
{
"name": "VIP Customers",
"description": "High-value customers for exclusive offers",
"settings": {
"doubleOptIn": true,
"welcomeEmail": true,
"confirmationEmail": "cmp_welcome123"
}
}
Add Contacts to List
POST /v1/lists/{id}/contacts
Request Body:
{
"contacts": [
"cnt_abc123",
"cnt_xyz789"
],
"skipConfirmation": false
}
Automations API
Create Automation
POST /v1/automations
Request Body:
{
"name": "Welcome Series",
"trigger": {
"type": "list_subscription",
"config": {
"listId": "lst_newsletter"
}
},
"steps": [
{
"type": "email",
"config": {
"campaignId": "cmp_welcome1",
"delay": 0
}
},
{
"type": "wait",
"config": {
"duration": 86400
}
},
{
"type": "email",
"config": {
"campaignId": "cmp_welcome2"
}
}
]
}
Automation Performance
GET /v1/automations/{id}/performance
Response:
{
"data": {
"enrolled": 5000,
"active": 1200,
"completed": 3750,
"stopped": 50,
"steps": [
{
"id": "step_1",
"type": "email",
"sent": 5000,
"opened": 3500,
"clicked": 1200
}
],
"conversionRate": 24.5,
"averageTimeToComplete": 604800
}
}
Rate Limiting
API requests are rate-limited to ensure fair usage:
- Standard Tier: 1,000 requests per hour
- Professional Tier: 5,000 requests per hour
- Enterprise Tier: Custom limits
Rate limit headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1643723400
Handling rate limits:
async function makeApiRequest(url, options) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
await sleep(retryAfter * 1000);
return makeApiRequest(url, options);
}
return response;
}
Error Handling
Standard error response format:
{
"error": {
"type": "validation_error",
"message": "Invalid request parameters",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
],
"request_id": "req_abc123xyz",
"documentation_url": "https://docs.nudgecampaign.com/api/errors#validation_error"
}
}
Common HTTP status codes:
200 OK: Request successful201 Created: Resource created204 No Content: Request successful, no content400 Bad Request: Invalid request401 Unauthorized: Authentication failed403 Forbidden: Insufficient permissions404 Not Found: Resource not found409 Conflict: Resource conflict429 Too Many Requests: Rate limit exceeded500 Internal Server Error: Server error
Webhook System
Webhook Overview
Webhooks provide real-time notifications for events in your NudgeCampaign account.
Webhook Configuration
Create Webhook Endpoint
POST /v1/webhooks
Request Body:
{
"url": "https://your-app.com/webhooks/nudgecampaign",
"events": [
"contact.created",
"contact.updated",
"contact.unsubscribed",
"campaign.sent",
"email.opened",
"email.clicked"
],
"headers": {
"X-Custom-Header": "custom-value"
},
"secret": "webhook_secret_key"
}
Webhook Events
Contact Events
contact.created
Triggered when a new contact is created:
{
"event": "contact.created",
"timestamp": "2024-03-15T10:30:00Z",
"data": {
"id": "cnt_abc123",
"email": "new@example.com",
"source": "api",
"lists": ["lst_newsletter"]
}
}
contact.updated
Triggered when contact information changes:
{
"event": "contact.updated",
"timestamp": "2024-03-15T10:35:00Z",
"data": {
"id": "cnt_abc123",
"changes": {
"firstName": {
"old": "John",
"new": "Jonathan"
},
"customFields.company": {
"old": "Old Corp",
"new": "New Corp"
}
}
}
}
contact.unsubscribed
Triggered when a contact unsubscribes:
{
"event": "contact.unsubscribed",
"timestamp": "2024-03-15T10:40:00Z",
"data": {
"id": "cnt_abc123",
"email": "user@example.com",
"reason": "user_request",
"campaignId": "cmp_xyz789",
"listId": "lst_newsletter"
}
}
Email Events
email.opened
Triggered when an email is opened:
{
"event": "email.opened",
"timestamp": "2024-03-15T11:00:00Z",
"data": {
"contactId": "cnt_abc123",
"campaignId": "cmp_xyz789",
"messageId": "msg_123456",
"userAgent": "Mozilla/5.0...",
"ipAddress": "192.168.1.1",
"location": {
"country": "US",
"region": "CA",
"city": "San Francisco"
}
}
}
email.clicked
Triggered when a link is clicked:
{
"event": "email.clicked",
"timestamp": "2024-03-15T11:05:00Z",
"data": {
"contactId": "cnt_abc123",
"campaignId": "cmp_xyz789",
"messageId": "msg_123456",
"url": "https://example.com/product",
"position": 3,
"userAgent": "Mozilla/5.0..."
}
}
Webhook Security
Signature Verification
Verify webhook authenticity using HMAC-SHA256:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Express.js middleware
app.post('/webhooks/nudgecampaign', (req, res) => {
const signature = req.headers['x-nudgecampaign-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
processWebhook(req.body);
res.status(200).send('OK');
});
Retry Logic
Webhook delivery follows exponential backoff:
- Attempt 1: Immediate
- Attempt 2: 1 minute
- Attempt 3: 5 minutes
- Attempt 4: 30 minutes
- Attempt 5: 2 hours
- Attempt 6: 6 hours
Third-Party Integrations
Salesforce Integration
Setup Process
Install NudgeCampaign Package
Navigate to Salesforce AppExchange Search for "NudgeCampaign" Click "Get It Now" Follow installation wizardConfigure Connected App
Setup > App Manager > New Connected App Name: NudgeCampaign Integration API Name: NudgeCampaign_Integration Enable OAuth Settings: Yes Callback URL: https://app.nudgecampaign.com/oauth/salesforce/callback Selected OAuth Scopes: - Access and manage your data (api) - Access your basic information (id, profile, email) - Perform requests on your behalf at any time (refresh_token, offline_access)Field Mapping Configuration
{ "fieldMappings": { "Contact": { "Email": "email", "FirstName": "firstName", "LastName": "lastName", "Account.Name": "customFields.company", "LeadSource": "customFields.source" }, "Lead": { "Email": "email", "FirstName": "firstName", "LastName": "lastName", "Company": "customFields.company", "LeadSource": "customFields.source" } } }
Data Synchronization
Real-time Sync (Apex Trigger)
trigger ContactSync on Contact (after insert, after update) {
List<NudgeCampaign.Contact> nudgeContacts = new List<NudgeCampaign.Contact>();
for (Contact c : Trigger.new) {
if (Trigger.isUpdate) {
Contact oldContact = Trigger.oldMap.get(c.Id);
if (c.Email != oldContact.Email ||
c.FirstName != oldContact.FirstName ||
c.LastName != oldContact.LastName) {
NudgeCampaign.Contact nc = new NudgeCampaign.Contact();
nc.email = c.Email;
nc.firstName = c.FirstName;
nc.lastName = c.LastName;
nc.salesforceId = c.Id;
nudgeContacts.add(nc);
}
} else {
NudgeCampaign.Contact nc = new NudgeCampaign.Contact();
nc.email = c.Email;
nc.firstName = c.FirstName;
nc.lastName = c.LastName;
nc.salesforceId = c.Id;
nudgeContacts.add(nc);
}
}
if (!nudgeContacts.isEmpty()) {
NudgeCampaign.API.syncContacts(nudgeContacts);
}
}
HubSpot Integration
OAuth Configuration
const HubSpotClient = require('@hubspot/api-client');
const hubspotClient = new HubSpotClient.Client({
accessToken: process.env.HUBSPOT_ACCESS_TOKEN,
});
// Sync contacts from HubSpot to NudgeCampaign
async function syncHubSpotContacts() {
try {
const response = await hubspotClient.crm.contacts.basicApi.getPage(
100,
undefined,
['email', 'firstname', 'lastname', 'company']
);
const contacts = response.results.map(contact => ({
email: contact.properties.email,
firstName: contact.properties.firstname,
lastName: contact.properties.lastname,
customFields: {
company: contact.properties.company,
hubspotId: contact.id
}
}));
// Bulk import to NudgeCampaign
await nudgeCampaignAPI.contacts.bulkCreate(contacts);
} catch (error) {
console.error('Sync failed:', error);
}
}
Shopify Integration
Webhook Setup
// Register Shopify webhooks
const webhookTopics = [
'customers/create',
'customers/update',
'orders/create',
'orders/fulfilled'
];
async function registerShopifyWebhooks(shop, accessToken) {
for (const topic of webhookTopics) {
const webhook = {
webhook: {
topic: topic,
address: `https://api.nudgecampaign.com/integrations/shopify/webhook`,
format: 'json'
}
};
await fetch(`https://${shop}/admin/api/2024-01/webhooks.json`, {
method: 'POST',
headers: {
'X-Shopify-Access-Token': accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify(webhook)
});
}
}
Order Tracking
// Process Shopify order webhook
async function processShopifyOrder(order) {
// Update contact in NudgeCampaign
const contact = {
email: order.email,
customFields: {
lastOrderDate: order.created_at,
totalOrders: order.order_number,
totalSpent: order.total_price,
averageOrderValue: order.total_price / order.line_items.length
},
tags: ['customer', 'shopify']
};
await nudgeCampaignAPI.contacts.upsert(contact);
// Trigger abandoned cart recovery if applicable
if (order.financial_status === 'pending') {
await nudgeCampaignAPI.automations.trigger('abandoned_cart', {
contactEmail: order.email,
cartUrl: order.abandoned_checkout_url,
cartValue: order.total_price,
items: order.line_items
});
}
}
Stripe Integration
Payment Events
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// Stripe webhook endpoint
app.post('/webhooks/stripe', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'customer.created':
await syncStripeCustomer(event.data.object);
break;
case 'customer.subscription.created':
await updateSubscriptionStatus(event.data.object);
break;
case 'invoice.payment_succeeded':
await recordPayment(event.data.object);
break;
case 'customer.subscription.deleted':
await handleChurn(event.data.object);
break;
}
res.json({received: true});
});
async function syncStripeCustomer(customer) {
const contact = {
email: customer.email,
customFields: {
stripeCustomerId: customer.id,
currency: customer.currency,
created: new Date(customer.created * 1000)
},
tags: ['stripe_customer']
};
await nudgeCampaignAPI.contacts.upsert(contact);
}
SDK Documentation
JavaScript/TypeScript SDK
Installation
npm install @nudgecampaign/sdk
Basic Usage
import { NudgeCampaign } from '@nudgecampaign/sdk';
const client = new NudgeCampaign({
apiKey: process.env.NUDGECAMPAIGN_API_KEY,
organizationId: process.env.ORGANIZATION_ID
});
// Create a contact
const contact = await client.contacts.create({
email: 'user@example.com',
firstName: 'John',
lastName: 'Doe',
customFields: {
company: 'Acme Corp'
}
});
// Send a campaign
const campaign = await client.campaigns.create({
name: 'Newsletter',
subject: 'Weekly Update',
content: {
html: '<h1>Hello {{firstName}}</h1>',
text: 'Hello {{firstName}}'
},
lists: ['lst_newsletter']
});
await client.campaigns.send(campaign.id);
Advanced Features
// Batch operations with error handling
try {
const results = await client.contacts.batch({
operations: [
{ method: 'create', data: { email: 'user1@example.com' } },
{ method: 'update', id: 'cnt_abc123', data: { firstName: 'Jane' } },
{ method: 'delete', id: 'cnt_xyz789' }
]
});
results.forEach((result, index) => {
if (result.error) {
console.error(`Operation ${index} failed:`, result.error);
}
});
} catch (error) {
console.error('Batch operation failed:', error);
}
// Stream large datasets
const contactStream = client.contacts.stream({
filter: { status: 'subscribed' },
batchSize: 100
});
contactStream.on('data', (contacts) => {
console.log(`Processing ${contacts.length} contacts`);
});
contactStream.on('error', (error) => {
console.error('Stream error:', error);
});
contactStream.on('end', () => {
console.log('Stream completed');
});
Python SDK
Installation
pip install nudgecampaign
Usage
from nudgecampaign import Client
from nudgecampaign.exceptions import NudgeCampaignError
client = Client(
api_key='YOUR_API_KEY',
organization_id='YOUR_ORG_ID'
)
# Create contact with error handling
try:
contact = client.contacts.create(
email='user@example.com',
first_name='John',
last_name='Doe',
custom_fields={'company': 'Acme Corp'}
)
print(f"Contact created: {contact.id}")
except NudgeCampaignError as e:
print(f"Error: {e.message}")
# Query contacts
contacts = client.contacts.list(
filter={'tags': 'customer'},
sort='created_at:desc',
limit=50
)
for contact in contacts:
print(f"{contact.email}: {contact.first_name} {contact.last_name}")
# Async operations
import asyncio
async def send_campaigns():
campaigns = await client.campaigns.list_async(status='draft')
tasks = []
for campaign in campaigns:
task = client.campaigns.send_async(campaign.id)
tasks.append(task)
results = await asyncio.gather(*tasks)
return results
asyncio.run(send_campaigns())
PHP SDK
Installation
composer require nudgecampaign/php-sdk
Usage
<?php
use NudgeCampaign\Client;
use NudgeCampaign\Resources\Contact;
use NudgeCampaign\Exceptions\ApiException;
$client = new Client([
'apiKey' => $_ENV['NUDGECAMPAIGN_API_KEY'],
'organizationId' => $_ENV['ORGANIZATION_ID']
]);
// Create contact
try {
$contact = $client->contacts->create([
'email' => 'user@example.com',
'firstName' => 'John',
'lastName' => 'Doe',
'customFields' => [
'company' => 'Acme Corp'
]
]);
echo "Contact created: " . $contact->id . "\n";
} catch (ApiException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
// List campaigns
$campaigns = $client->campaigns->list([
'status' => 'sent',
'from_date' => '2024-01-01',
'limit' => 10
]);
foreach ($campaigns as $campaign) {
$analytics = $client->campaigns->analytics($campaign->id);
echo sprintf(
"%s: Sent %d, Opens %d (%.1f%%)\n",
$campaign->name,
$analytics->sent,
$analytics->opened,
$analytics->metrics->openRate
);
}
Data Synchronization
Synchronization Patterns
Real-time Sync
// WebSocket connection for real-time updates
const WebSocket = require('ws');
const ws = new WebSocket('wss://sync.nudgecampaign.com/v1/stream', {
headers: {
'Authorization': `Bearer ${API_KEY}`,
'X-Organization-ID': ORG_ID
}
});
ws.on('open', () => {
// Subscribe to events
ws.send(JSON.stringify({
action: 'subscribe',
events: ['contact.*', 'campaign.*']
}));
});
ws.on('message', (data) => {
const event = JSON.parse(data);
switch (event.type) {
case 'contact.created':
syncContactToExternalSystem(event.data);
break;
case 'campaign.sent':
updateCampaignMetrics(event.data);
break;
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
// Implement reconnection logic
});
Batch Synchronization
// Scheduled batch sync job
const cron = require('node-cron');
cron.schedule('0 */4 * * *', async () => {
console.log('Starting batch synchronization');
const lastSyncTime = await getLastSyncTime();
// Export modified contacts
const modifiedContacts = await client.contacts.list({
filter: `updated_at > ${lastSyncTime}`,
limit: 1000
});
// Process in chunks
const chunks = chunk(modifiedContacts, 100);
for (const contactChunk of chunks) {
await syncContactsToExternalSystem(contactChunk);
}
await updateLastSyncTime(new Date());
console.log('Batch synchronization completed');
});
Conflict Resolution
// Conflict resolution strategies
class ConflictResolver {
constructor(strategy = 'last_write_wins') {
this.strategy = strategy;
}
async resolve(localData, remoteData) {
switch (this.strategy) {
case 'last_write_wins':
return this.lastWriteWins(localData, remoteData);
case 'merge':
return this.mergeChanges(localData, remoteData);
case 'manual':
return this.requireManualResolution(localData, remoteData);
default:
throw new Error(`Unknown strategy: ${this.strategy}`);
}
}
lastWriteWins(local, remote) {
const localTime = new Date(local.updatedAt);
const remoteTime = new Date(remote.updatedAt);
return localTime > remoteTime ? local : remote;
}
mergeChanges(local, remote) {
// Deep merge with field-level timestamps
const merged = { ...local };
for (const field in remote) {
if (remote[field]._updated > local[field]._updated) {
merged[field] = remote[field];
}
}
return merged;
}
async requireManualResolution(local, remote) {
// Queue for manual review
await queueConflict({
type: 'data_conflict',
local: local,
remote: remote,
timestamp: new Date()
});
throw new Error('Manual resolution required');
}
}
Integration Patterns
Event-Driven Architecture
// Event bus for decoupled integrations
class IntegrationEventBus {
constructor() {
this.handlers = new Map();
}
on(event, handler) {
if (!this.handlers.has(event)) {
this.handlers.set(event, []);
}
this.handlers.get(event).push(handler);
}
async emit(event, data) {
const eventHandlers = this.handlers.get(event) || [];
const results = await Promise.allSettled(
eventHandlers.map(handler => handler(data))
);
results.forEach((result, index) => {
if (result.status === 'rejected') {
console.error(
`Handler ${index} for ${event} failed:`,
result.reason
);
}
});
}
}
const eventBus = new IntegrationEventBus();
// Register integration handlers
eventBus.on('contact.created', async (contact) => {
await syncToSalesforce(contact);
await addToMailchimp(contact);
await updateAnalytics(contact);
});
eventBus.on('order.completed', async (order) => {
await updateContactLifetimeValue(order);
await triggerPostPurchaseFlow(order);
});
Data Transformation
// Transform data between systems
class DataTransformer {
constructor(mappings) {
this.mappings = mappings;
}
transform(source, targetSystem) {
const mapping = this.mappings[targetSystem];
if (!mapping) {
throw new Error(`No mapping for ${targetSystem}`);
}
const transformed = {};
for (const [sourceField, targetField] of Object.entries(mapping.fields)) {
const value = this.getNestedValue(source, sourceField);
if (value !== undefined) {
const transformedValue = mapping.transforms?.[sourceField]
? mapping.transforms[sourceField](value)
: value;
this.setNestedValue(transformed, targetField, transformedValue);
}
}
return transformed;
}
getNestedValue(obj, path) {
return path.split('.').reduce((acc, part) => acc?.[part], obj);
}
setNestedValue(obj, path, value) {
const parts = path.split('.');
const last = parts.pop();
const nested = parts.reduce((acc, part) => {
if (!acc[part]) acc[part] = {};
return acc[part];
}, obj);
nested[last] = value;
}
}
// Usage
const transformer = new DataTransformer({
salesforce: {
fields: {
'email': 'Email',
'firstName': 'FirstName',
'lastName': 'LastName',
'customFields.company': 'Account.Name',
'tags': 'Tags__c'
},
transforms: {
'tags': (tags) => tags.join(';')
}
}
});
const salesforceContact = transformer.transform(nudgeContact, 'salesforce');
Testing & Debugging
Integration Testing
// Test webhook endpoints
const request = require('supertest');
const app = require('./app');
describe('Webhook Integration', () => {
test('handles contact.created webhook', async () => {
const webhook = {
event: 'contact.created',
data: {
id: 'cnt_test123',
email: 'test@example.com'
}
};
const signature = generateWebhookSignature(webhook);
const response = await request(app)
.post('/webhooks/nudgecampaign')
.set('X-NudgeCampaign-Signature', signature)
.send(webhook);
expect(response.status).toBe(200);
// Verify side effects
const contact = await getContactFromDatabase('test@example.com');
expect(contact).toBeDefined();
expect(contact.nudgeId).toBe('cnt_test123');
});
test('rejects invalid webhook signature', async () => {
const response = await request(app)
.post('/webhooks/nudgecampaign')
.set('X-NudgeCampaign-Signature', 'invalid')
.send({ event: 'test' });
expect(response.status).toBe(401);
});
});
Debugging Tools
// Request/Response logger middleware
class IntegrationLogger {
constructor(options = {}) {
this.logLevel = options.logLevel || 'info';
this.logFile = options.logFile || './integration.log';
}
logRequest(request) {
const log = {
timestamp: new Date().toISOString(),
type: 'request',
method: request.method,
url: request.url,
headers: this.sanitizeHeaders(request.headers),
body: this.sanitizeBody(request.body)
};
this.writeLog(log);
}
logResponse(response) {
const log = {
timestamp: new Date().toISOString(),
type: 'response',
status: response.status,
headers: response.headers,
body: this.sanitizeBody(response.body),
duration: response.duration
};
this.writeLog(log);
}
sanitizeHeaders(headers) {
const sanitized = { ...headers };
if (sanitized.authorization) {
sanitized.authorization = 'Bearer [REDACTED]';
}
return sanitized;
}
sanitizeBody(body) {
if (!body) return body;
const sanitized = JSON.parse(JSON.stringify(body));
const sensitiveFields = ['password', 'apiKey', 'secret'];
const sanitizeObject = (obj) => {
for (const key in obj) {
if (sensitiveFields.includes(key)) {
obj[key] = '[REDACTED]';
} else if (typeof obj[key] === 'object') {
sanitizeObject(obj[key]);
}
}
};
sanitizeObject(sanitized);
return sanitized;
}
writeLog(log) {
console.log(JSON.stringify(log));
// Also write to file
fs.appendFileSync(this.logFile, JSON.stringify(log) + '\n');
}
}
Migration Guides
Migrating from MailChimp
Step 1: Export MailChimp Data
const Mailchimp = require('@mailchimp/mailchimp_marketing');
Mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: process.env.MAILCHIMP_SERVER_PREFIX,
});
async function exportMailchimpData() {
// Export lists
const lists = await Mailchimp.lists.getAllLists();
for (const list of lists.lists) {
// Export list members
const members = await Mailchimp.lists.getListMembersInfo(
list.id,
{ count: 1000 }
);
// Transform to NudgeCampaign format
const contacts = members.members.map(member => ({
email: member.email_address,
firstName: member.merge_fields.FNAME,
lastName: member.merge_fields.LNAME,
status: member.status === 'subscribed' ? 'subscribed' : 'unsubscribed',
tags: member.tags.map(t => t.name),
customFields: member.merge_fields,
mailchimpId: member.id
}));
// Import to NudgeCampaign
await nudgeCampaignAPI.contacts.bulkCreate(contacts);
// Export campaigns
const campaigns = await Mailchimp.campaigns.list({ list_id: list.id });
for (const campaign of campaigns.campaigns) {
await migrateCampaign(campaign);
}
}
}
Step 2: Map Custom Fields
const fieldMapping = {
'FNAME': 'firstName',
'LNAME': 'lastName',
'COMPANY': 'customFields.company',
'PHONE': 'customFields.phone',
'ADDRESS': 'customFields.address',
'BIRTHDAY': 'customFields.birthday'
};
function mapMailchimpFields(mergeFields) {
const mapped = {};
for (const [mcField, value] of Object.entries(mergeFields)) {
const nudgeField = fieldMapping[mcField];
if (nudgeField) {
if (nudgeField.includes('.')) {
const [parent, child] = nudgeField.split('.');
if (!mapped[parent]) mapped[parent] = {};
mapped[parent][child] = value;
} else {
mapped[nudgeField] = value;
}
}
}
return mapped;
}
Migrating from SendGrid
const sgClient = require('@sendgrid/client');
sgClient.setApiKey(process.env.SENDGRID_API_KEY);
async function migrateSendGridContacts() {
// Export all contacts
const [response] = await sgClient.request({
method: 'POST',
url: '/v3/marketing/contacts/exports',
body: {
file_type: 'json',
max_file_size: 5000
}
});
// Poll for export completion
const exportId = response.body.id;
let exportReady = false;
while (!exportReady) {
const [status] = await sgClient.request({
method: 'GET',
url: `/v3/marketing/contacts/exports/${exportId}`
});
if (status.body.status === 'ready') {
exportReady = true;
const downloadUrl = status.body.urls[0];
// Download and process contacts
const contacts = await downloadExport(downloadUrl);
// Transform and import
const nudgeContacts = contacts.map(contact => ({
email: contact.email,
firstName: contact.first_name,
lastName: contact.last_name,
customFields: contact.custom_fields,
lists: contact.list_ids,
createdAt: contact.created_at,
updatedAt: contact.updated_at
}));
await nudgeCampaignAPI.contacts.bulkCreate(nudgeContacts);
}
await sleep(5000); // Wait 5 seconds before polling again
}
}
Best Practices
API Usage Best Practices
- Use Batch Operations: For bulk operations, use batch endpoints instead of making individual requests
- Implement Retry Logic: Handle transient failures with exponential backoff
- Cache Responses: Cache frequently accessed data to reduce API calls
- Use Webhooks: Prefer webhooks over polling for real-time updates
- Paginate Large Datasets: Always paginate when fetching large amounts of data
Security Best Practices
- Secure API Keys: Never expose API keys in client-side code
- Use HTTPS: Always use HTTPS for API communication
- Validate Webhooks: Always verify webhook signatures
- Implement Rate Limiting: Respect rate limits to avoid service disruption
- Audit Integration Access: Regularly review and audit integration permissions
Data Management Best Practices
- Handle Duplicates: Implement deduplication logic for contact syncing
- Map Fields Carefully: Document and maintain field mappings between systems
- Monitor Sync Status: Track synchronization status and handle failures
- Backup Before Migration: Always backup data before major migrations
- Test in Staging: Test integrations in a staging environment first
Troubleshooting
Common Integration Issues
Authentication Failures
- Verify API key is correct and active
- Check organization ID matches
- Ensure proper OAuth scopes are granted
- Verify webhook secrets match
Data Sync Issues
- Check field mapping configuration
- Verify data types match between systems
- Ensure required fields are populated
- Monitor rate limits and quotas
Webhook Delivery Failures
- Verify endpoint URL is accessible
- Check SSL certificate validity
- Ensure response times are under 30 seconds
- Implement proper error handling
Debug Checklist
- Check API Logs: Review API request/response logs
- Verify Credentials: Ensure all credentials are valid
- Test Connectivity: Verify network connectivity
- Review Error Messages: Check detailed error responses
- Monitor Rate Limits: Check rate limit headers
- Validate Data Format: Ensure data meets API requirements
- Check Webhook Logs: Review webhook delivery attempts
- Test in Isolation: Test integration components separately
Support Resources
Documentation
- API Reference: https://docs.nudgecampaign.com/api
- SDK Documentation: https://docs.nudgecampaign.com/sdk
- Integration Guides: https://docs.nudgecampaign.com/integrations
- Webhook Events: https://docs.nudgecampaign.com/webhooks
Developer Tools
- API Explorer: https://api.nudgecampaign.com/explorer
- Webhook Tester: https://app.nudgecampaign.com/webhooks/test
- SDK Playground: https://playground.nudgecampaign.com
- Status Page: https://status.nudgecampaign.com
Community & Support
- Developer Forum: https://forum.nudgecampaign.com/developers
- GitHub: https://github.com/nudgecampaign
- Stack Overflow: Tag
nudgecampaign - Support: developers@nudgecampaign.com
Conclusion
This integration documentation provides comprehensive guidance for connecting NudgeCampaign with your existing systems. Whether you're using our REST API, webhooks, SDKs, or pre-built integrations, you have the tools needed to create powerful, automated marketing workflows.
Remember to follow best practices for security, error handling, and data management. Our support team is available to help with complex integration scenarios or custom requirements.
For the latest updates and new integration features, subscribe to our developer newsletter at developers.nudgecampaign.com/newsletter.