Call Tracking Metrics - Authentication
๐ Overviewโ
Call Tracking Metrics uses username/password authentication (Basic Auth) rather than API keys. Credentials are stored securely in MongoDB and used to generate access_key and secret tokens for API requests. The system includes automatic token invalidation detection, soft delete functionality, and campaign preservation logic.
Source Files:
- Controller:
external/Integrations/CallTrackingMetrics/Controllers/auth.js - Provider:
external/Integrations/CallTrackingMetrics/Providers/auth-api.js - Model:
external/Integrations/CallTrackingMetrics/Models/keys.js - Routes:
external/Integrations/CallTrackingMetrics/Routes/auth.js
External API: POST https://api.calltrackingmetrics.com/api/v1/authentication
๐๏ธ Collections Usedโ
integrations.calltrackingmetrics.keyโ
- Operations: Create, Read, Update, Delete
- Model:
shared/models/calltrackingmetrics-key.js - Usage: Store CTM credentials with account association
Document Structure:
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"account_id": ObjectId("507f1f77bcf86cd799439012"),
"user": "ctm_username",
"password": "encrypted_password",
"access_key": "ctm_access_key_abc123",
"secret": "ctm_secret_xyz789",
"token_invalidated": false,
"deleted": ISODate("2023-10-01T12:00:00Z"), // Soft delete timestamp
"createdAt": ISODate("2023-09-15T10:00:00Z"),
"updatedAt": ISODate("2023-10-01T12:00:00Z")
}
analytics.calltrackingmetrics.userconfigโ
- Operations: Create, Read, Update, Delete
- Model:
shared/models/analytics-calltrackingmetrics-userconfig.js - Usage: Link CTM account to DashClicks for campaign tracking
Document Structure:
{
"_id": ObjectId,
"account_id": ObjectId,
"token": "507f1f77bcf86cd799439011", // Reference to key document ID
"calltracking_account_id": "act_123456",
"calltracking_sub_account_id": "sub_789", // Optional
"createdAt": ISODate,
"updatedAt": ISODate
}
๐ Data Flowโ
Authentication Flowโ
sequenceDiagram
participant Client as DashClicks Frontend
participant Controller as Auth Controller
participant Provider as Auth API Provider
participant CTM as CTM API
participant KeysDB as MongoDB (keys)
Client->>Controller: POST /auth {username, password}
Controller->>KeysDB: Check existing credentials
alt Credentials exist and valid
KeysDB-->>Controller: Return existing credentials
Controller-->>Client: Return docId
else Credentials invalidated
KeysDB-->>Controller: token_invalidated: true
Controller->>KeysDB: Delete invalidated credentials
Controller->>Provider: authenticate(username, password)
Provider->>CTM: POST /authentication
CTM-->>Provider: {access_key, secret}
Provider-->>Controller: Tokens
Controller->>KeysDB: Save new credentials
KeysDB-->>Controller: New credential document
Controller-->>Client: Return docId
else No credentials exist
KeysDB-->>Controller: KEYS_NOT_FOUND
Controller->>Provider: authenticate(username, password)
Provider->>CTM: POST /authentication
CTM-->>Provider: {access_key, secret}
Provider-->>Controller: Tokens
Controller->>KeysDB: Save credentials
KeysDB-->>Controller: Credential document
Controller-->>Client: Return docId
end
๐ง Business Logic & Functionsโ
Controller Functionsโ
postApiKey(req, res, next)โ
Purpose: Authenticate and save CTM credentials
Source: Controllers/auth.js
External API: POST https://api.calltrackingmetrics.com/api/v1/authentication
Parameters:
req.body.username(String, required) - CTM usernamereq.body.password(String, required) - CTM passwordreq.auth.account_id(ObjectId) - DashClicks account ID
Returns: JSON response with credential document ID
{
"success": true,
"message": "SUCCESS",
"data": {
"docId": "507f1f77bcf86cd799439011"
}
}
Business Logic Flow:
-
Validate Account Access
- Call
checkAccountAccess(req)to verify permissions - Return 400 if invalid account ID
- Call
-
Check Existing Credentials
- Query database for existing credentials
- Exclude soft-deleted documents (
deleted: { $exists: false })
-
Handle Invalidated Credentials
- If credentials exist with
token_invalidated: true - Delete all invalidated credentials for account
- Proceed to authenticate new credentials
- If credentials exist with
-
Return Existing Valid Credentials
- If credentials exist and not invalidated
- Return existing docId without re-authenticating
- Avoids unnecessary API calls
-
Authenticate New Credentials (if no valid credentials exist)
- Validate username and password provided
- Call CTM authentication API
- Receive
access_keyandsecretfrom CTM - Save credentials with tokens to database
- Return docId
Request Example:
POST /v1/e/calltrackingmetrics/auth
Authorization: Bearer {jwt_token}
Content-Type: application/json
{
"username": "ctm_user@example.com",
"password": "secure_password_123"
}
Success Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"docId": "507f1f77bcf86cd799439011"
}
}
Error Response (Invalid Credentials):
{
"success": false,
"errno": 400,
"message": "Invalid Keys Entered",
"code": "INVALID_KEYS_ENTERED"
}
Error Response (Missing Fields):
{
"success": false,
"errno": 400,
"message": "username and password are required."
}
Authentication API Call:
The provider calls CTM authentication endpoint:
POST /api/v1/authentication
Content-Type: multipart/form-data
user={username}
password={password}
CTM API Response:
{
"access_key": "ctm_access_key_abc123",
"secret": "ctm_secret_xyz789"
}
Error Handling:
- Invalid Account: Returns 400 with error message
- Invalid Credentials: Returns 400 with
INVALID_KEYS_ENTERED - Missing Fields: Returns 400 with descriptive message
- CTM API Error: 401 response triggers error handling
Side Effects:
- โ ๏ธ Database Write: Creates new credential document
- โ ๏ธ Credential Deletion: Deletes invalidated credentials
- โ ๏ธ External API Call: Authenticates with CTM API
- โน๏ธ Idempotent: Returns existing credentials without re-authenticating
saveUserAnalyticsConfig(req, res, next)โ
Purpose: Link CTM account to DashClicks for campaign tracking
Source: Controllers/auth.js
External API: N/A (database operation only)
Parameters:
req.body.token(String, required) - Credential document IDreq.body.account_id(String, required) - CTM account IDreq.body.subaccount_id(String, optional) - CTM sub-account IDreq.auth.account_id(ObjectId) - DashClicks account ID
Returns: JSON response with upsert result
{
"success": true,
"message": "SUCCESS",
"data": {
"acknowledged": true,
"modifiedCount": 1
}
}
Business Logic Flow:
-
Validate Account Access
- Verify account permissions
-
Find Account Owner
- Query User collection for account owner
- Get notification preferences from Config
-
Validate Required Fields
- Check token and account_id provided
- Return 400 if missing
-
Upsert Configuration
- Create or update user config document
- Link CTM account/sub-account to DashClicks account
-
Send Notification
- If enabled, send FCM notification about integration added
- Notification type:
project_added
Request Example:
POST /v1/e/calltrackingmetrics/auth/config
Authorization: Bearer {jwt_token}
Content-Type: application/json
{
"token": "507f1f77bcf86cd799439011",
"account_id": "act_123456",
"subaccount_id": "sub_789"
}
Success Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"acknowledged": true,
"modifiedCount": 1,
"upsertedId": null,
"upsertedCount": 0,
"matchedCount": 1
}
}
Error Response (Missing Fields):
{
"success": false,
"errno": 400,
"message": "account id and token are required."
}
Notification Sent:
{
"title": "Call Tracking Metrics integration added",
"body": "A new Call Tracking Metrics integration has been added.",
"data": { "subType": "bell" },
"module": "projects",
"type": "project_added"
}
Error Handling:
- Invalid Account: Returns 400 with error message
- Missing Fields: Returns 400 with descriptive message
- Notification Failure: Logged but doesn't fail request
Side Effects:
- โ ๏ธ Database Upsert: Creates or updates config document
- โ ๏ธ FCM Notification: Sends push notification to account owner
- โน๏ธ Idempotent: Upsert ensures only one config per account
deleteApiKey(req, res, next)โ
Purpose: Delete CTM credentials with campaign preservation logic
Source: Controllers/auth.js
External API: N/A (database operations only)
Parameters:
req.query.global(String, optional) - "true" for complete removalreq.auth.account_id(ObjectId) - Account IDreq.auth.uid(ObjectId) - User IDreq.auth.parent_account(ObjectId, optional) - Parent account
Returns: JSON success response
{
"success": true,
"message": "SUCCESS"
}
Business Logic Flow:
-
Validate Account Access
- Verify account permissions
-
Find User and Preferences
- Get user document and notification preferences
-
Check Global Delete Flag
If
global=true(Complete Removal):a. Disable Active Campaigns
- Mark all CTM campaigns as deleted
b. Delete User Config
- Remove analytics config document
c. Soft Delete Credentials
- Update credentials with
deleted: new Date() - Credentials remain in database but marked deleted
d. Send Notifications
- FCM notification (browser/bell if enabled)
- Email notification (if enabled)
- Send to parent account if sub-account
If
global=falseor not provided (Conditional Delete):a. Check Existing Campaigns
- Query for active CTM campaigns
b. If Campaigns Exist
- Only delete user config
- Keep credentials intact for existing campaigns
- Send notifications
c. If No Campaigns
- Delete user config
- Soft delete credentials
- Send notifications
Request Examples:
# Complete removal (delete everything)
DELETE /v1/e/calltrackingmetrics/auth?global=true
Authorization: Bearer {jwt_token}
# Conditional removal (preserve if campaigns exist)
DELETE /v1/e/calltrackingmetrics/auth
Authorization: Bearer {jwt_token}
Success Response:
{
"success": true,
"message": "SUCCESS"
}
Notification Examples:
FCM Notification:
{
"title": "Call Tracking Metrics integration is Disconnected",
"body": "Call Tracking Metrics integration has been disconnected.",
"data": { "subType": "bell" },
"module": "analytics",
"type": "integration_disconnected"
}
Email Notification:
{
"subject": "Call Tracking Metrics integration disconnected",
"content": "Call Tracking Metrics integration has been disconnected.",
"origin": "analytics",
"recipients": [{
"name": "John Doe",
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe"
}]
}
Parent Account Notification:
{
"title": "Call Tracking Metrics Integration for projects Disconnected",
"body": "Call Tracking Metrics Integration for projects has been disconnected.",
"data": { "subType": "bell" },
"module": "analytics",
"type": "integration_disconnected"
}
Error Handling:
- Invalid Account: Returns 400 with error message
- Database Errors: Passed to error middleware
- Notification Failures: Logged but don't fail request
Side Effects:
- โ ๏ธ Campaign Updates: May mark campaigns as deleted
- โ ๏ธ Config Deletion: Removes user config document
- โ ๏ธ Soft Delete: Marks credentials as deleted (not removed)
- โ ๏ธ Notifications: Sends email and FCM notifications
- โ ๏ธ Parent Account: May send notifications to parent account
Model Functionsโ
save(authData)โ
Purpose: Save new credential document
Source: Models/keys.js
Parameters:
authData(Object) - Credential datauser(String) - CTM usernamepassword(String) - CTM passwordaccess_key(String) - CTM access keysecret(String) - CTM secretaccount_id(ObjectId) - Account ID
Returns: Promise<Object> - Saved document with ID
{
"docId": "507f1f77bcf86cd799439011",
"id": "507f1f77bcf86cd799439011",
"account_id": ObjectId,
"user": "ctm_user",
"access_key": "...",
"secret": "..."
}
Side Effects:
- โ ๏ธ Database Write: Creates document in calltrackingmetrics.key collection
findApiKey(condition)โ
Purpose: Find credential document by conditions
Source: Models/keys.js
Parameters:
condition(Object) - Query conditionsaccount_id(ObjectId) - Account ID_id(ObjectId, optional) - Document ID
Returns: Promise<Object> - Credential document or throws error
Error Handling:
- Throws
KEYS_NOT_FOUNDerror if no document found
Side Effects:
- โน๏ธ Database Read: Queries calltrackingmetrics.key collection
deleteAll(accountId)โ
Purpose: Delete all credentials for account (hard delete)
Source: Models/keys.js
Parameters:
accountId(ObjectId) - Account ID
Returns: Promise<Object> - Delete result
Side Effects:
- โ ๏ธ Database Delete: Permanently removes documents
update(condition, updateData)โ
Purpose: Update multiple credential documents
Source: Models/keys.js
Parameters:
condition(Object) - Query conditionsupdateData(Object) - Update operations
Returns: Promise<Object> - Update result
Example Usage:
// Soft delete
await keysModel.update(
{ account_id: new mongoose.Types.ObjectId(accountId) },
{ deleted: new Date() },
);
// Mark as invalidated
await keysModel.update(
{ account_id: new mongoose.Types.ObjectId(accountId) },
{ $set: { token_invalidated: true } },
);
Side Effects:
- โ ๏ธ Database Update: Modifies multiple documents
Provider Functionsโ
authenticateUser({ user, accountId, password })โ
Purpose: Authenticate with CTM API to get access tokens
Source: Providers/auth-api.js
External API: POST https://api.calltrackingmetrics.com/api/v1/authentication
Parameters:
user(String) - CTM usernamepassword(String) - CTM passwordaccountId(ObjectId) - DashClicks account ID (not used in API call)
Returns: Promise<Object> - CTM authentication response
{
"access_key": "ctm_access_key_abc123",
"secret": "ctm_secret_xyz789"
}
HTTP Request:
POST /api/v1/authentication
Host: api.calltrackingmetrics.com
Content-Type: multipart/form-data
user={username}
password={password}
Side Effects:
- โน๏ธ External API Call: Authenticates with CTM
๐ Integration Pointsโ
Campaign Integrationโ
Credential Preservation Logic:
- Credentials preserved if active campaigns exist
- Prevents breaking campaign tracking mid-flight
- Only user config deleted when campaigns active
Campaign Check:
const existingCampaigns = await campaignModel.findOne({
account_id: accountId,
is_deleted: false,
integration: 'calltrackingmetrics',
});
if (existingCampaigns) {
// Only delete user config, keep credentials
} else {
// Safe to delete both config and credentials
}
Notification Systemโ
Integration Points:
- FCM notifications for integration connect/disconnect
- Email notifications based on user preferences
- Parent account notifications for sub-accounts
Notification Types:
project_added- When integration addedintegration_disconnected- When integration removed
๐งช Edge Cases & Special Handlingโ
Token Invalidationโ
Issue: CTM API returns 401 when credentials invalid
Handling:
- Main error handler in
index.jscatches 401 errors - Sets
token_invalidated: trueon all account credentials - User must re-authenticate with new credentials
Soft Delete vs Hard Deleteโ
Issue: Need to preserve credentials for active campaigns
Handling:
- Soft Delete: Set
deletedtimestamp, credentials remain - Hard Delete:
deleteAll()removes documents completely - Soft delete used by default to preserve historical data
Multiple Credentials Per Accountโ
Issue: System designed for one credential set per account
Handling:
findApiKeyusesfindOne()- returns first matchdeleteAllremoves all credentials when invalidated- UI should prevent creating multiple valid credentials
โ ๏ธ Important Notesโ
- ๐ Basic Auth: Uses username/password, not static API keys
- ๐ Access Tokens: CTM returns access_key and secret after authentication
- ๐๏ธ Soft Delete: Credentials marked as deleted, not removed
- ๐ Campaign Preservation: Credentials kept if active campaigns exist
- ๐ Token Invalidation: Automatic detection on 401 errors
- ๐ Notifications: Email and FCM notifications on connect/disconnect
- ๐ฅ Parent Account: Sub-accounts send notifications to parent
- โ ๏ธ Global Delete: Use
global=truequery param for complete removal - ๐ Form Data: Authentication uses multipart/form-data encoding
๐ Related Documentationโ
- Integration Overview: Call Tracking Metrics Integration
- Accounts: Account Management
- Calls: Call Tracking & Analytics
- CTM API: Call Tracking Metrics API Documentation