Constant Contact Integration
🌐 Overview
Constant Contact integration providing access to email marketing lists and contact management through OAuth 2.0 authentication. Supports automatic token refresh and paginated contact export.
Provider: Constant Contact (https://www.constantcontact.com)
API Version: v3
Integration Type: OAuth 2.0 with automatic refresh token management
📚 Documentation Structure
This integration is organized into the following sections:
- Authentication - OAuth 2.0 flow with automatic token refresh
- Contact Lists - Contact export with cursor-based pagination
🔧 Features
- ✅ OAuth 2.0: Secure authentication with automatic token refresh
- ✅ Contact Management: Export all contacts with list memberships
- ✅ Auto Token Refresh: Automatic refresh before each API call
- ✅ Cursor Pagination: Navigate large datasets efficiently
- ✅ Sub-account Support: Works with DashClicks sub-accounts
- ✅ Token Management: Automatic refresh and invalidation handling
📊 Architecture
Frontend Request
↓
OAuth Flow
↓
Token Storage (MongoDB)
↓
Auto Token Refresh
↓
Constant Contact API v3
↓
Contact Data with List Memberships
🗄️ MongoDB Collections
📚 Detailed Schema: See Database Collections Documentation
constant_contact.keys
Purpose: Store OAuth 2.0 tokens with refresh capability
Key Fields:
token(Object) - Token informationaccess_token(String) - OAuth access token (expires)refresh_token(String) - Refresh token (long-lived)expires_in(Number) - Token expiration time (seconds)token_type(String) - Token type (Bearer)scope(String) - OAuth scopes granted
account_id(String) - DashClicks account IDowner(String) - DashClicks user IDgenerated_at(Number) - Unix timestamp of token generationtoken_invalidated(Boolean) - Token invalidation flag
Indexes:
{ account_id: 1, owner: 1 }(unique) - Primary lookup
Schema: Flexible schema (strict: false) with timestamps
📁 Directory Structure
Source Code Location:
external/Integrations/ConstantContact/
├── Controllers/
│ ├── authController.js # OAuth handlers
│ └── contactController.js # Contact operations
├── Providers/
│ ├── api.js # OAuth API calls
│ └── contactData.js # Contact API calls
├── Models/
│ └── keys.js # Database operations
├── Routes/
│ ├── authRoutes.js # Auth endpoints
│ └── contactRoutes.js # Contact endpoints
└── index.js # Route registration
Shared Models Used:
shared/models/constant-contact-key.js
🚀 Quick Start
1. Configure Environment Variables
# OAuth Configuration
CONSTANTCONTACT_CLIENT_ID=bd5e80bb-7317-4b22-8839-031b8a9c8417
CONSTANTCONTACT_SECRET_ID=0MtgSHFWiMix3g4pEaKXvg
CONSTANTCONTACT_REDIRECT_URL=https://api.dashclicks.com/v1/integrations/constantcontact/auth/callback
# OAuth Endpoints
CONSTANTCONTACT_AUTH_ENDPOINT=https://api.cc.email/v3/idfed
CONSTANTCONTACT_ACCESS_TOKEN_ENDPOINT=https://idfed.constantcontact.com/as/token.oauth2
CONSTANTCONTACT_GRANT_TYPE=authorization_code
# API Endpoints
CONSTANTCONTACT_CONTACT_ENDPOINT=https://api.cc.email/v3/contacts?include_count=true&include=list_memberships
# Scopes
CONSTANTCONTACT_AUTH_SCOPE=contact_data
2. Initiate OAuth Flow
GET /v1/integrations/constantcontact/auth/login?forward_url=https://app.dashclicks.com/integrations
Authorization: Bearer {jwt_token}
3. Export Contacts
GET /v1/integrations/constantcontact/lists
Authorization: Bearer {jwt_token}
4. Paginated Export
GET /v1/integrations/constantcontact/lists?limit=100&page={cursor_value}
Authorization: Bearer {jwt_token}
📖 API Endpoints Summary
| Method | Endpoint | Description |
|---|---|---|
| GET | /auth/login | Initiate OAuth 2.0 flow |
| GET | /auth/callback | Handle OAuth callback |
| DELETE | /auth | Delete stored access token |
| GET | /lists | Export contacts with pagination |
🔑 OAuth Scopes
Available Scopes
| Scope | Description |
|---|---|
contact_data | Access to contact information and list memberships |
campaign_data | Access to email campaigns (not implemented) |
account_read | Read account information (not implemented) |
Current Implementation: Uses only contact_data scope
📊 Token Management
Token Lifecycle
-
Initial Token: Obtained during OAuth callback
access_token: Valid for 24 hoursrefresh_token: Long-lived (no expiration)
-
Automatic Refresh: Before each API call
- Check
generated_attimestamp - If expired, use refresh token to get new access token
- Update MongoDB with new tokens
- Check
-
Token Storage: Stored with generation timestamp
{
token: {
access_token: "...",
refresh_token: "...",
expires_in: 86400, // 24 hours
token_type: "Bearer",
scope: "contact_data"
},
generated_at: 1696934400 // Unix timestamp
}
Token Expiration
Access tokens expire after 24 hours. The integration automatically:
- Detects expired tokens
- Uses refresh token to obtain new access token
- Updates database with new token and timestamp
- Continues with API request
📊 Pagination
Constant Contact uses cursor-based pagination:
Query Parameters:
limit(Integer) - Records per page (default: 20, max: 500)page(String) - Cursor value for next/previous page
Response Format:
{
"success": true,
"message": "SUCCESS",
"data": [...],
"pagination": {
"next": "cursor_string_for_next_page",
"prev": "cursor_string_for_prev_page"
}
}
Navigation Pattern:
# First page
GET /lists?limit=100
# Next page (use cursor from response)
GET /lists?limit=100&page={next_cursor}
# Previous page (use cursor from response)
GET /lists?limit=100&page={prev_cursor}
⚠️ Important Notes
- 🔄 Auto Refresh: Access tokens refreshed automatically before each request
- ⏱️ Token Lifetime: Access tokens valid for 24 hours
- 🔁 Refresh Tokens: Never expire, stored securely
- 📊 Pagination: Cursor-based (not page numbers)
- 👤 Single Connection: One credential set per user/account pair
- ⚡ Performance: Automatic token refresh adds minimal latency (~200ms)
🔗 Related Documentation
🎯 Common Use Cases
Initial Contact Sync
GET /v1/integrations/constantcontact/lists?limit=500
Export all contacts in batches of 500 for initial sync to DashClicks.
Incremental Updates
# First batch
GET /v1/integrations/constantcontact/lists?limit=100
# Next batch using cursor from previous response
GET /v1/integrations/constantcontact/lists?limit=100&page={next_cursor}
Progressive loading for large contact lists.
🐛 Troubleshooting
"Forward url required"
Missing forward_url query parameter in login request.
"Access Token Not Found"
No credentials stored. Initiate OAuth flow first.
"Unauthorized User"
JWT token missing or invalid. Check authentication.
Token Refresh Failures
Refresh token may be revoked. User must re-authenticate.
📈 Performance Considerations
- Token Refresh: Adds ~200ms latency on first request after expiration
- Pagination: Use limit=500 for fastest bulk exports
- Rate Limits: Constant Contact limits vary by plan
- Caching: Consider caching contact data for frequently accessed accounts
- Batch Processing: Use queue-based processing for 10,000+ contacts
🔒 Security Features
JWT State Token
- Expiry: 2 hours
- Secret:
process.env.APP_SECRET - Claims: Account ID, User ID, Forward URL
- Purpose: Prevent CSRF attacks
Token Encryption
- Tokens stored in MongoDB
- Should be encrypted at rest (deployment-specific)
- Access controlled by DashClicks authentication
Single Connection
Only one active connection per user/account to prevent token proliferation.