SalesForce
This guide walks you through the steps required to connect Docflo.ai to your Salesforce org, enabling seamless document integration and automated lead creation from processed documents.
- Salesforce org with API access enabled
- Visual Studio or development environment for .NET
- Docflo.ai account with API access
- Salesforce user with appropriate permissions
- SSL certificate management capabilities
π Integration Stepsβ
Follow these steps to establish a secure connection between Docflo.ai and your Salesforce org:
Step 1: Import SSL Certificate Chainβ
- Download our SSL certificate chain (link)
- Import the certificate to the Windows Certificate Store on your integration server
- Add the certificate to Trusted Root Certification Authorities
- Ensure the certificate is properly validated for HTTPS connections
Step 2: Configure Salesforce Connected Appβ
- Navigate to Setup in your Salesforce org
- Go to App Manager (Apps β App Manager)
- Click "New Connected App" and configure:
- Connected App Name: Docflo Integration
- API Name: Docflo_Integration
- Contact Email: Your email address
- Enable OAuth Settings: Check this box
- Callback URL:
https://login.salesforce.com/services/oauth2/success - Selected OAuth Scopes:
- Access and manage your data (api)
- Perform requests on your behalf at any time (refresh_token, offline_access)
- Access your basic information (id, profile, email, address, phone)
- Save the Connected App
- Copy Consumer Key and Consumer Secret for later use
Step 3: Generate API Credentialsβ
- Go to the "Integrations" section in your Docflo.ai platform
- Create an API key for Salesforce integration
- Copy the API key and store it securely
- Copy the tenant ID as well - you'll need both for the integration
Step 4: Create Salesforce Integration Serviceβ
Create a Windows Service or scheduled application using Salesforce REST API with OAuth. Here's a sample C# implementation:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Linq;
namespace DocfloSalesforceIntegration
{
public class DocfloSalesforceIntegrationService
{
private readonly string _docfloApiUrl;
private readonly string _tenantId;
private readonly string _apiKey;
private readonly string _salesforceLoginUrl;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _username;
private readonly string _password;
private readonly string _securityToken;
private readonly HttpClient _httpClient;
private readonly HttpClient _salesforceClient;
private string _accessToken;
private string _instanceUrl;
public DocfloSalesforceIntegrationService(string docfloApiUrl, string tenantId, string apiKey,
string salesforceLoginUrl, string clientId, string clientSecret,
string username, string password, string securityToken)
{
_docfloApiUrl = docfloApiUrl;
_tenantId = tenantId;
_apiKey = apiKey;
_salesforceLoginUrl = salesforceLoginUrl;
_clientId = clientId;
_clientSecret = clientSecret;
_username = username;
_password = password;
_securityToken = securityToken;
// Initialize Docflo HTTP client
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("x-tenant-id", _tenantId);
_httpClient.DefaultRequestHeaders.Add("apiKey", _apiKey);
// Initialize Salesforce HTTP client
_salesforceClient = new HttpClient();
}
public async Task<bool> AuthenticateWithSalesforce()
{
try
{
var authUrl = $"{_salesforceLoginUrl}/services/oauth2/token";
var authData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("client_id", _clientId),
new KeyValuePair<string, string>("client_secret", _clientSecret),
new KeyValuePair<string, string>("username", _username),
new KeyValuePair<string, string>("password", _password + _securityToken)
};
var content = new FormUrlEncodedContent(authData);
var response = await _salesforceClient.PostAsync(authUrl, content);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var authResponse = JsonConvert.DeserializeObject<SalesforceAuthResponse>(responseContent);
_accessToken = authResponse.AccessToken;
_instanceUrl = authResponse.InstanceUrl;
// Set authorization header for future requests
_salesforceClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", _accessToken);
Console.WriteLine("Successfully authenticated with Salesforce");
return true;
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Salesforce authentication failed: {response.StatusCode}");
Console.WriteLine($"Error details: {errorContent}");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during Salesforce authentication: {ex.Message}");
return false;
}
}
public async Task<List<DocfloDocument>> GetDocfloDocuments(string documentType, bool includeResults = true)
{
try
{
string url = $"{_docfloApiUrl}/docs/v1/document?type={documentType}&includeResults={includeResults}";
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string jsonContent = await response.Content.ReadAsStringAsync();
var docfloResponse = JsonConvert.DeserializeObject<DocfloResponse>(jsonContent);
return docfloResponse.Data;
}
catch (Exception ex)
{
Console.WriteLine($"Error fetching Docflo documents: {ex.Message}");
return new List<DocfloDocument>();
}
}
public async Task<bool> CreateLead(DocfloDocument document)
{
try
{
// Extract data from Docflo document results
var extractedData = ExtractLeadData(document);
// Create lead payload
var leadPayload = new
{
FirstName = extractedData.FirstName,
LastName = extractedData.LastName ?? "Unknown",
Company = extractedData.Company ?? "Unknown Company",
Email = extractedData.Email,
Phone = extractedData.Phone,
Street = extractedData.Street,
City = extractedData.City,
State = extractedData.State,
PostalCode = extractedData.PostalCode,
Country = extractedData.Country,
LeadSource = "Docflo Integration",
Status = "New",
Description = $"Lead created from Docflo document {document.Id}. " +
$"Document type: {document.SourceDesc}. " +
$"Created: {document.CreatedAt}",
// Custom fields for Docflo tracking
Docflo_Document_ID__c = document.Id,
Docflo_Source_ID__c = document.SourceId,
Docflo_Created_Date__c = document.CreatedAt
};
string jsonPayload = JsonConvert.SerializeObject(leadPayload, Formatting.Indented);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
string salesforceUrl = $"{_instanceUrl}/services/data/v58.0/sobjects/Lead/";
HttpResponseMessage response = await _salesforceClient.PostAsync(salesforceUrl, content);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
var createResponse = JsonConvert.DeserializeObject<SalesforceCreateResponse>(responseContent);
Console.WriteLine($"Lead created successfully for document {document.Id}");
Console.WriteLine($"Salesforce Lead ID: {createResponse.Id}");
return true;
}
else
{
string errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Error creating Lead: {response.StatusCode}");
Console.WriteLine($"Error details: {errorContent}");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception creating Lead: {ex.Message}");
return false;
}
}
public async Task<bool> CreateContact(DocfloDocument document)
{
try
{
// Extract data from Docflo document results
var extractedData = ExtractLeadData(document);
// Create contact payload
var contactPayload = new
{
FirstName = extractedData.FirstName,
LastName = extractedData.LastName ?? "Unknown",
Email = extractedData.Email,
Phone = extractedData.Phone,
MailingStreet = extractedData.Street,
MailingCity = extractedData.City,
MailingState = extractedData.State,
MailingPostalCode = extractedData.PostalCode,
MailingCountry = extractedData.Country,
LeadSource = "Docflo Integration",
Description = $"Contact created from Docflo document {document.Id}. " +
$"Document type: {document.SourceDesc}. " +
$"Created: {document.CreatedAt}",
// Custom fields for Docflo tracking
Docflo_Document_ID__c = document.Id,
Docflo_Source_ID__c = document.SourceId,
Docflo_Created_Date__c = document.CreatedAt
};
string jsonPayload = JsonConvert.SerializeObject(contactPayload, Formatting.Indented);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
string salesforceUrl = $"{_instanceUrl}/services/data/v58.0/sobjects/Contact/";
HttpResponseMessage response = await _salesforceClient.PostAsync(salesforceUrl, content);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
var createResponse = JsonConvert.DeserializeObject<SalesforceCreateResponse>(responseContent);
Console.WriteLine($"Contact created successfully for document {document.Id}");
Console.WriteLine($"Salesforce Contact ID: {createResponse.Id}");
return true;
}
else
{
string errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Error creating Contact: {response.StatusCode}");
Console.WriteLine($"Error details: {errorContent}");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception creating Contact: {ex.Message}");
return false;
}
}
private SalesforceLeadData ExtractLeadData(DocfloDocument document)
{
var data = new SalesforceLeadData();
// Extract data from Docflo results
if (document.DocfloResults?.ModelFields?.Items?.Value != null)
{
foreach (var item in document.DocfloResults.ModelFields.Items.Value)
{
var description = item.ValueObject?.Description;
if (description != null)
{
switch (description.Type?.ToLower())
{
case "first_name":
case "given_name":
data.FirstName = description.ValueString;
break;
case "last_name":
case "family_name":
case "surname":
data.LastName = description.ValueString;
break;
case "full_name":
case "name":
// Split full name if first/last not provided separately
if (string.IsNullOrEmpty(data.FirstName) && string.IsNullOrEmpty(data.LastName))
{
var nameParts = description.ValueString?.Split(' ');
if (nameParts?.Length >= 2)
{
data.FirstName = nameParts[0];
data.LastName = string.Join(" ", nameParts.Skip(1));
}
else if (nameParts?.Length == 1)
{
data.LastName = nameParts[0];
}
}
break;
case "company":
case "company_name":
case "organization":
data.Company = description.ValueString;
break;
case "email":
case "email_address":
data.Email = description.ValueString;
break;
case "phone":
case "phone_number":
case "telephone":
data.Phone = description.ValueString;
break;
case "address":
case "street":
case "street_address":
data.Street = description.ValueString;
break;
case "city":
data.City = description.ValueString;
break;
case "state":
case "province":
data.State = description.ValueString;
break;
case "zip_code":
case "postal_code":
data.PostalCode = description.ValueString;
break;
case "country":
data.Country = description.ValueString;
break;
}
}
}
}
return data;
}
public void Dispose()
{
_httpClient?.Dispose();
_salesforceClient?.Dispose();
}
}
// Data models
public class DocfloResponse
{
[JsonProperty("data")]
public List<DocfloDocument> Data { get; set; }
}
public class DocfloDocument
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("sourceId")]
public string SourceId { get; set; }
[JsonProperty("sourceDesc")]
public string SourceDesc { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("numOfPages")]
public string NumOfPages { get; set; }
[JsonProperty("createdAt")]
public string CreatedAt { get; set; }
[JsonProperty("docflo_results")]
public DocfloResults DocfloResults { get; set; }
}
public class DocfloResults
{
[JsonProperty("modelFields")]
public ModelFields ModelFields { get; set; }
}
public class ModelFields
{
[JsonProperty("Items")]
public ItemsField Items { get; set; }
}
public class ItemsField
{
[JsonProperty("value")]
public List<Item> Value { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}
public class Item
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("valueObject")]
public ValueObject ValueObject { get; set; }
}
public class ValueObject
{
[JsonProperty("Description")]
public Description Description { get; set; }
}
public class Description
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("valueString")]
public string ValueString { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
public class SalesforceAuthResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("instance_url")]
public string InstanceUrl { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("issued_at")]
public string IssuedAt { get; set; }
[JsonProperty("signature")]
public string Signature { get; set; }
}
public class SalesforceCreateResponse
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("errors")]
public List<object> Errors { get; set; }
}
public class SalesforceLeadData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
// Main program example
class Program
{
static async Task Main(string[] args)
{
var service = new DocfloSalesforceIntegrationService(
"https://api.docflo.ai", // Replace with actual Docflo API URL
"your-tenant-id", // Replace with your tenant ID
"your-api-key", // Replace with your API key
"https://login.salesforce.com", // Salesforce login URL
"your-consumer-key", // Connected App Consumer Key
"your-consumer-secret", // Connected App Consumer Secret
"your-salesforce-username", // Salesforce username
"your-salesforce-password", // Salesforce password
"your-security-token" // Salesforce security token
);
try
{
// Authenticate with Salesforce
bool authenticated = await service.AuthenticateWithSalesforce();
if (authenticated)
{
// Get documents from Docflo
var documents = await service.GetDocfloDocuments("contact_form", true);
foreach (var document in documents)
{
// Process each document based on status
if (document.Status == "processed")
{
// Create Lead for each processed document
bool leadResult = await service.CreateLead(document);
// Alternatively, create Contact instead of Lead
// bool contactResult = await service.CreateContact(document);
if (leadResult)
{
Console.WriteLine($"Successfully created lead for document {document.Id}");
}
}
}
}
else
{
Console.WriteLine("Failed to authenticate with Salesforce");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error in main process: {ex.Message}");
}
finally
{
service.Dispose();
}
}
}
}
π§ Configuration Requirementsβ
NuGet Packages Requiredβ
Add these NuGet packages to your project:
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
Salesforce Custom Fields Setupβ
Create these custom fields in Salesforce for tracking Docflo documents:
-
Lead Object:
-
Field Name:
Docflo_Document_ID__c -
Type: Text (255)
-
Label: Docflo Document ID
-
Field Name:
Docflo_Source_ID__c -
Type: Text (255)
-
Label: Docflo Source ID
-
Field Name:
Docflo_Created_Date__c -
Type: Text (255)
-
Label: Docflo Created Date
-
-
Contact Object (if using contacts):
- Same custom fields as above
Application Configurationβ
Create an app.config file with your settings:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="DocfloApiUrl" value="https://api.docflo.ai" />
<add key="TenantId" value="your-tenant-id" />
<add key="ApiKey" value="your-api-key" />
<add key="SalesforceLoginUrl" value="https://login.salesforce.com" />
<add key="SalesforceConsumerKey" value="your-consumer-key" />
<add key="SalesforceConsumerSecret" value="your-consumer-secret" />
<add key="SalesforceUsername" value="your-salesforce-username" />
<add key="SalesforcePassword" value="your-salesforce-password" />
<add key="SalesforceSecurityToken" value="your-security-token" />
</appSettings>
</configuration>
π Deployment Optionsβ
Option 1: Windows Serviceβ
Deploy as a Windows Service for continuous processing:
- Install the service using
sc createor InstallUtil - Configure service account with appropriate permissions
- Set up logging for monitoring and troubleshooting
- Schedule regular document polling
Option 2: Azure Functionβ
Deploy as an Azure Function for cloud-based processing:
- Create Azure Function App
- Configure timer trigger for scheduled execution
- Set application settings for configuration values
- Monitor execution through Azure portal
Option 3: Heroku Schedulerβ
Deploy on Heroku with scheduled tasks:
- Create Heroku application
- Configure Heroku Scheduler add-on
- Set environment variables for configuration
- Monitor through Heroku dashboard
π Monitoring and Loggingβ
Implement comprehensive logging using NLog or Serilog:
// Add to your service class
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public void LogIntegrationActivity(string message, Exception ex = null)
{
if (ex == null)
{
logger.Info(message);
}
else
{
logger.Error(ex, message);
}
}
π OAuth Security Best Practicesβ
- Store credentials securely using Azure Key Vault or similar
- Implement token refresh logic for long-running services
- Use IP restrictions in Salesforce Connected App settings
- Monitor API usage to stay within limits
- Implement proper error handling for authentication failures
π Integration Complete! Your Salesforce org is now connected to Docflo.ai and can automatically create leads or contacts from processed documents, enabling seamless lead generation from your document processing workflow.
π Troubleshootingβ
Common Issuesβ
-
OAuth Authentication Errors:
- Verify Consumer Key and Consumer Secret
- Check username, password, and security token
- Ensure Connected App is properly configured
- Verify user has API access enabled
-
SSL Certificate Issues:
- Verify certificate installation in Windows Certificate Store
- Check certificate chain completeness
- Ensure proper certificate binding
-
API Rate Limiting:
- Monitor daily API limits in Salesforce
- Implement retry logic with exponential backoff
- Consider bulk API for large volumes
-
Field Mapping Issues:
- Validate extracted data from Docflo documents
- Check required fields for Lead/Contact creation
- Ensure custom fields exist in Salesforce
-
Security Token Issues:
- Reset security token if authentication fails
- Ensure security token is appended to password
- Check for IP restrictions in user profile
π Supportβ
For technical assistance with the Salesforce integration:
- Contact Docflo.ai support team
- Consult Salesforce Developer documentation
- Review Salesforce REST API guides
- Contact your Salesforce administrator