Skip to main content

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.

πŸ“‹ Prerequisites:
  • 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​

  1. Download our SSL certificate chain (link)
  2. Import the certificate to the Windows Certificate Store on your integration server
  3. Add the certificate to Trusted Root Certification Authorities
  4. Ensure the certificate is properly validated for HTTPS connections

Step 2: Configure Salesforce Connected App​

  1. Navigate to Setup in your Salesforce org
  2. Go to App Manager (Apps β†’ App Manager)
  3. 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)
  4. Save the Connected App
  5. Copy Consumer Key and Consumer Secret for later use

Step 3: Generate API Credentials​

  1. Go to the "Integrations" section in your Docflo.ai platform
  2. Create an API key for Salesforce integration
  3. Copy the API key and store it securely
  4. 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:

  1. 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

  2. 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:

  1. Install the service using sc create or InstallUtil
  2. Configure service account with appropriate permissions
  3. Set up logging for monitoring and troubleshooting
  4. Schedule regular document polling

Option 2: Azure Function​

Deploy as an Azure Function for cloud-based processing:

  1. Create Azure Function App
  2. Configure timer trigger for scheduled execution
  3. Set application settings for configuration values
  4. Monitor execution through Azure portal

Option 3: Heroku Scheduler​

Deploy on Heroku with scheduled tasks:

  1. Create Heroku application
  2. Configure Heroku Scheduler add-on
  3. Set environment variables for configuration
  4. 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​

  1. Store credentials securely using Azure Key Vault or similar
  2. Implement token refresh logic for long-running services
  3. Use IP restrictions in Salesforce Connected App settings
  4. Monitor API usage to stay within limits
  5. 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​

  1. 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
  2. SSL Certificate Issues:

    • Verify certificate installation in Windows Certificate Store
    • Check certificate chain completeness
    • Ensure proper certificate binding
  3. API Rate Limiting:

    • Monitor daily API limits in Salesforce
    • Implement retry logic with exponential backoff
    • Consider bulk API for large volumes
  4. Field Mapping Issues:

    • Validate extracted data from Docflo documents
    • Check required fields for Lead/Contact creation
    • Ensure custom fields exist in Salesforce
  5. 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