Skip to main content

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 username
  • req.body.password (String, required) - CTM password
  • req.auth.account_id (ObjectId) - DashClicks account ID

Returns: JSON response with credential document ID

{
"success": true,
"message": "SUCCESS",
"data": {
"docId": "507f1f77bcf86cd799439011"
}
}

Business Logic Flow:

  1. Validate Account Access

    • Call checkAccountAccess(req) to verify permissions
    • Return 400 if invalid account ID
  2. Check Existing Credentials

    • Query database for existing credentials
    • Exclude soft-deleted documents (deleted: { $exists: false })
  3. Handle Invalidated Credentials

    • If credentials exist with token_invalidated: true
    • Delete all invalidated credentials for account
    • Proceed to authenticate new credentials
  4. Return Existing Valid Credentials

    • If credentials exist and not invalidated
    • Return existing docId without re-authenticating
    • Avoids unnecessary API calls
  5. Authenticate New Credentials (if no valid credentials exist)

    • Validate username and password provided
    • Call CTM authentication API
    • Receive access_key and secret from 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 ID
  • req.body.account_id (String, required) - CTM account ID
  • req.body.subaccount_id (String, optional) - CTM sub-account ID
  • req.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:

  1. Validate Account Access

    • Verify account permissions
  2. Find Account Owner

    • Query User collection for account owner
    • Get notification preferences from Config
  3. Validate Required Fields

    • Check token and account_id provided
    • Return 400 if missing
  4. Upsert Configuration

    • Create or update user config document
    • Link CTM account/sub-account to DashClicks account
  5. 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 removal
  • req.auth.account_id (ObjectId) - Account ID
  • req.auth.uid (ObjectId) - User ID
  • req.auth.parent_account (ObjectId, optional) - Parent account

Returns: JSON success response

{
"success": true,
"message": "SUCCESS"
}

Business Logic Flow:

  1. Validate Account Access

    • Verify account permissions
  2. Find User and Preferences

    • Get user document and notification preferences
  3. 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=false or 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 data
    • user (String) - CTM username
    • password (String) - CTM password
    • access_key (String) - CTM access key
    • secret (String) - CTM secret
    • account_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 conditions
    • account_id (ObjectId) - Account ID
    • _id (ObjectId, optional) - Document ID

Returns: Promise<Object> - Credential document or throws error

Error Handling:

  • Throws KEYS_NOT_FOUND error 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 conditions
  • updateData (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 username
  • password (String) - CTM password
  • accountId (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 added
  • integration_disconnected - When integration removed

๐Ÿงช Edge Cases & Special Handlingโ€‹

Token Invalidationโ€‹

Issue: CTM API returns 401 when credentials invalid

Handling:

  • Main error handler in index.js catches 401 errors
  • Sets token_invalidated: true on 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 deleted timestamp, 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:

  • findApiKey uses findOne() - returns first match
  • deleteAll removes 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=true query param for complete removal
  • ๐Ÿ”’ Form Data: Authentication uses multipart/form-data encoding

๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:30 AM