Skip to main content

🔌 Salesforce Integration

🎯 Overview

Salesforce is the world's leading enterprise CRM platform providing comprehensive customer relationship management, sales automation, service cloud, and analytics. The DashClicks integration enables OAuth-based authentication and data export for contacts, opportunities (deals), and notes from Salesforce CRM.

Provider: Salesforce, Inc. (https://www.salesforce.com)

API Version: v48.0 (configurable)

Integration Type: OAuth 2.0 REST API with SOQL (Salesforce Object Query Language)

📁 Directory Structure

Documentation Structure:

salesforce/
├── 📄 index.md - Integration overview and setup
├── 📄 authentication.md - OAuth 2.0 flow and token management
└── 📄 crm-data.md - CRM data export (contacts, deals, notes) with pagination

Source Code Location:

  • Base Path: external/Integrations/Salesforce/
  • Controllers: Controllers/authController.js, Controllers/exportController.js, Controllers/pagination.js
  • Providers: Providers/api.js
  • Routes: Routes/authRoutes.js, Routes/exportRoutes.js
  • Models: Models/keys.js

🗄️ MongoDB Collections

📚 Detailed Schema: See Database Collections Documentation

integrations.salesforce.key

  • Purpose: Store OAuth access tokens and refresh tokens for Salesforce API authentication
  • Model: shared/models/salesforce-key.js
  • Primary Use: Persist Salesforce OAuth credentials for authenticated API requests

Schema Structure:

{
_id: ObjectId,
account_id: String, // DashClicks account ID
owner: String, // User ID who connected the integration
token: {
access_token: String, // Short-lived access token
refresh_token: String, // Long-lived refresh token (never expires unless revoked)
issued_at: String // Timestamp when token was issued (Unix timestamp as string)
},
token_invalidated: Boolean // Flag indicating manual token invalidation
}
Schema Flexibility

The Salesforce key model uses {strict: false} schema option, allowing additional fields from Salesforce OAuth responses to be stored dynamically.

🔐 Authentication & Configuration

Authentication Method: OAuth 2.0 Authorization Code Grant

Required Environment Variables:

VariableDescriptionRequired
SALESFORCE_CLIENT_IDOAuth application client ID
SALESFORCE_SECRET_IDOAuth application client secret
SALESFORCE_AUTH_SCOPESOAuth permission scopes
SALESFORCE_AUTH_URLOAuth authorization endpoint
SALESFORCE_TOKEN_URLOAuth token exchange endpoint
SALESFORCE_GRANT_TYPEGrant type (authorization_code)
SALESFORCE_REDIRECT_URLOAuth callback URL
SALESFORCE_CONTACT_ENDPOINTSOQL query for contacts
SALESFORCE_DEALS_ENDPOINTSOQL query for opportunities
SALESFORCE_NOTES_ENDPOINTSOQL query for notes
SALESFORCE_COUNT_ENDPOINTBase endpoint for COUNT queries

Example Configuration:

SALESFORCE_CLIENT_ID=3MVG97quAmFZJfVxWTwCFgzaduZWToVcHwRjxykW0U4.k_KVogJz1IsKlf7HXupdgw0eWCja8iOUZ91JQ9R98
SALESFORCE_SECRET_ID=CC7AC2A9674C0A7E25D159FA868DFE7399D462CC7D58D61F1D558D23E97B34A8
SALESFORCE_REDIRECT_URL=http://localhost:5000/v1/integrations/salesforce/auth/callback
SALESFORCE_AUTH_SCOPES=api%20id%20web%20refresh_token
SALESFORCE_AUTH_URL=https://login.salesforce.com/services/oauth2/authorize
SALESFORCE_TOKEN_URL=https://login.salesforce.com/services/oauth2/token
SALESFORCE_GRANT_TYPE=authorization_code

# SOQL Query Endpoints (v48.0)
SALESFORCE_CONTACT_ENDPOINT=https://na1.salesforce.com/services/data/v48.0/query/?q=SELECT+name,+account.name,+title,+phone,+email,+contact.owner.alias+from+contact
SALESFORCE_DEALS_ENDPOINT=https://na1.salesforce.com/services/data/v48.0/query?q=select+name,+account.name,+amount,+CloseDate,+StageName,+opportunity.owner.alias+from+opportunity
SALESFORCE_NOTES_ENDPOINT=https://na1.salesforce.com/services/data/v48.0/query?q=select+body,+IsDeleted,+IsPrivate,+OwnerId,+ParentId,+Title,+note.owner.alias+from+note
SALESFORCE_COUNT_ENDPOINT=https://ap5.salesforce.com/services/data/v48.0/query?q=SELECT+count()+FROM

Credential Storage: OAuth tokens stored in integrations.salesforce.key collection

Instance-Specific URLs

Salesforce uses instance-specific URLs (e.g., na1, ap5). Update endpoints based on your Salesforce org's instance.

🏗️ Architecture Overview

Key Responsibilities:

  • OAuth Authentication: Manage OAuth 2.0 authorization flow with JWT state tokens (1-hour expiration)
  • SOQL Queries: Execute Salesforce Object Query Language queries for data retrieval
  • Pagination: Custom pagination system with page/limit parameters and total record counts
  • Token Refresh: Automatic refresh token handling when access tokens expire
  • Data Export: Retrieve contacts, opportunities (deals), and notes with custom field selection

API Communication Pattern:

  • Protocol: REST API over HTTPS
  • Authentication: Bearer token in Authorization header
  • Query Language: SOQL (Salesforce Object Query Language) via REST API
  • Data Format: JSON response
  • Pagination: Offset-based with LIMIT and OFFSET clauses in SOQL

Rate Limiting:

  • Salesforce Limits: Varies by edition (Enterprise: 1,000 API calls/24h per user, Unlimited: 5,000+)
  • Handling: No automatic rate limiting in current implementation
  • Recommendation: Monitor API usage via Salesforce Setup → System Overview

🔗 Features & Capabilities

Core Features

🔄 Integration Data Flow

sequenceDiagram
participant Client as DashClicks Frontend
participant Router as API Router
participant Auth as Auth Controller
participant Export as Export Controller
participant Provider as Salesforce Provider
participant DB as MongoDB
participant Salesforce as Salesforce API

Client->>Router: GET /v1/integrations/salesforce/auth/login
Router->>Auth: Verify scope & access
Auth->>DB: Check existing token
alt Token exists and valid
Auth-->>Client: Redirect to success URL
else Token missing/invalid
Auth->>Auth: Generate JWT state token (1h exp)
Auth-->>Client: Redirect to Salesforce OAuth
Client->>Salesforce: User authorizes app
Salesforce-->>Auth: Redirect with code
Auth->>Provider: Exchange code for tokens
Provider->>Salesforce: POST /oauth/v1/token
Salesforce-->>Provider: Access + refresh tokens
Provider->>DB: Store tokens
Auth-->>Client: Redirect to success URL
end

Note over Client,Salesforce: Data Export Flow

Client->>Router: GET /export/contacts?page=1&limit=100
Router->>Export: exportData()
Export->>DB: Fetch stored token
DB-->>Export: Return access token
Export->>Salesforce: GET /query (COUNT query)
Salesforce-->>Export: Total record count
Export->>Export: Calculate pagination
Export->>Salesforce: GET /query (with LIMIT + OFFSET)
alt Token valid
Salesforce-->>Export: Contact records
Export-->>Client: Data + pagination
else Token expired (401)
Export->>Provider: getRefreshAccessToken()
Provider->>Salesforce: POST /token (refresh grant)
Salesforce-->>Provider: New access token
Provider->>DB: Update token
Export->>Salesforce: Retry query with new token
Salesforce-->>Export: Contact records
Export-->>Client: Data + pagination
end

🔗 API Endpoints

Authentication Endpoints

EndpointMethodScopeDescription
/v1/integrations/salesforce/auth/loginGETsalesforce, salesforce.createInitialize OAuth flow
/v1/integrations/salesforce/auth/callbackGETNoneOAuth callback (auto-redirect)
/v1/integrations/salesforce/auth/DELETEsalesforce, salesforce.deleteDelete OAuth token

Data Export Endpoints

EndpointMethodScopeQuery ParamsDescription
/v1/integrations/salesforce/export/:typeGETsalesforce, salesforce.readpage, limitExport CRM data

Supported Types:

  • contacts - Export Salesforce contacts
  • deals - Export Salesforce opportunities (deals)
  • notes - Export Salesforce notes

Query Parameters:

  • page (Number, optional) - Page number (default: 1)
  • limit (Number, optional) - Records per page (default: all records)
Dynamic Type Routing

Export endpoint uses dynamic :type parameter, allowing a single route to handle multiple CRM data types.

📊 Response Format

All data export endpoints return a standardized response structure:

{
"success": true,
"message": "SUCCESS",
"data": [...], // Array of CRM records
"pagination": {
"page": 1,
"per_page": 100,
"total": 1500,
"total_pages": 15,
"next_page": 2,
"prev_page": null,
"offsetValue": 0
}
}

Pagination Fields:

  • page - Current page number
  • per_page - Records per page
  • total - Total number of records across all pages
  • total_pages - Total number of pages
  • next_page - Next page number (null if last page)
  • prev_page - Previous page number (null if first page)
  • offsetValue - SOQL OFFSET value for current page

🎯 SOQL Query Structure

Contacts Query

SELECT name, account.name, title, phone, email, contact.owner.alias
FROM contact
LIMIT {per_page} OFFSET {offsetValue}

Fields Retrieved:

  • name - Contact full name
  • account.name - Associated account name (relationship)
  • title - Job title
  • phone - Phone number
  • email - Email address
  • contact.owner.alias - Owner alias (user who owns the contact)

Opportunities (Deals) Query

SELECT name, account.name, amount, CloseDate, StageName, opportunity.owner.alias
FROM opportunity
LIMIT {per_page} OFFSET {offsetValue}

Fields Retrieved:

  • name - Opportunity name
  • account.name - Associated account name
  • amount - Deal amount (currency)
  • CloseDate - Expected close date
  • StageName - Current sales stage
  • opportunity.owner.alias - Owner alias

Notes Query

SELECT body, IsDeleted, IsPrivate, OwnerId, ParentId, Title, note.owner.alias
FROM note
LIMIT {per_page} OFFSET {offsetValue}

Fields Retrieved:

  • body - Note content/text
  • IsDeleted - Soft delete flag
  • IsPrivate - Privacy flag
  • OwnerId - ID of user who created the note
  • ParentId - ID of related record (contact, account, etc.)
  • Title - Note title
  • note.owner.alias - Owner alias
Custom Fields

To retrieve custom fields, modify the SOQL queries in environment variables. Custom fields end with __c (e.g., Custom_Field__c).

🚨 Error Handling

Common Error Scenarios:

Error CodeScenarioHandling
401Invalid/expired access tokenAutomatic token refresh attempted
404Token not found in databaseUser redirected to re-authenticate
400Invalid type parameterError message with supported types
400Invalid SOQL querySalesforce error returned to client

Salesforce-Specific Errors:

// INVALID_SESSION_ID (401)
[
{
"message": "Session expired or invalid",
"errorCode": "INVALID_SESSION_ID"
}
]

Token Invalidation:

When token_invalidated: true is set on a stored token, the integration automatically deletes the token and forces re-authentication.

📊 Monitoring & Logging

Token Lifecycle Tracking:

  • issued_at field tracks when tokens were issued (Unix timestamp)
  • Refresh tokens never expire unless manually revoked
  • Token expiration managed automatically via refresh token flow

Recommended Monitoring:

  • Track OAuth callback failures
  • Monitor Salesforce API usage limits (Setup → System Overview)
  • Alert on 401 errors indicating token issues
  • Log SOQL query performance

🎯 Integration Use Cases

Contact Synchronization

Import Salesforce contacts with pagination:

GET /v1/integrations/salesforce/export/contacts?page=1&limit=200

Opportunity Pipeline Analysis

Export all opportunities for reporting:

GET /v1/integrations/salesforce/export/deals?page=1&limit=500

Activity History

Fetch all notes for contact history:

GET /v1/integrations/salesforce/export/notes?page=1&limit=100

⚠️ Implementation Notes

OAuth State Management

  • JWT tokens used for state parameter with 1-hour expiration
  • State includes account_id, owner, and forward_url
  • Prevents CSRF attacks by validating state on callback

Token Refresh Strategy

  • Access tokens expire after session timeout (configurable in Salesforce org)
  • Refresh tokens never expire unless manually revoked
  • Automatic refresh on 401 INVALID_SESSION_ID error
  • Updated tokens saved back to database immediately

Pagination System

  • Custom pagination utility (Controllers/pagination.js)
  • Requires two API calls: COUNT query + data query
  • LIMIT and OFFSET clauses appended to SOQL queries
  • Default behavior: Return all records if limit not specified

SOQL Considerations

  • Field Selection: Only specified fields are retrieved
  • Relationships: Use dot notation (e.g., account.name)
  • Custom Fields: End with __c suffix
  • API Version: Specified in endpoint URLs (v48.0)

Scope Requirements

All endpoints require one of:

  • salesforce scope - Full Salesforce integration access
  • salesforce.create scope - Create integration
  • salesforce.read scope - Read data
  • salesforce.delete scope - Delete integration

These scopes are enforced via verifyScope middleware.

🚀 Getting Started

1. Configure OAuth App

Create Connected App in Salesforce Setup and configure environment variables with callback URL.

2. Initiate OAuth Flow

Direct users to:

GET /v1/integrations/salesforce/auth/login?forward_url=https://app.dashclicks.com/success

3. Export Data

Once authenticated, export data using:

GET /v1/integrations/salesforce/export/{type}?page=1&limit=100

Where {type} is one of: contacts, deals, notes

4. Handle Pagination

Use returned pagination.next_page for subsequent requests:

GET /v1/integrations/salesforce/export/contacts?page=2&limit=100

Continue until pagination.next_page is null.

5. Customize SOQL Queries

Modify environment variables to add custom fields:

SALESFORCE_CONTACT_ENDPOINT=https://na1.salesforce.com/services/data/v48.0/query/?q=SELECT+name,+email,+Custom_Field__c+from+contact

📈 Performance Considerations

API Usage Optimization

  • Use appropriate limit values to reduce API call count
  • Salesforce charges per API call - monitor usage in Setup
  • Consider caching frequently accessed data
  • Use bulk queries when possible

SOQL Query Optimization

  • Select only required fields (avoid SELECT * equivalent)
  • Use indexed fields in WHERE clauses when filtering
  • Avoid complex relationship queries when possible
  • Consider Salesforce governor limits (50,000 rows max)

Token Management

  • Refresh tokens are long-lived - store securely
  • Access tokens are session-based - refresh on 401 errors
  • Consider implementing token revocation on user logout
💬

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