Seamless Salesforce Integration: Creating Leads with Attachments using Python and Flask

Integration with Salesforce using Python is a powerful capability, 

particularly when leveraging the simplicity of the Simple Salesforce API. 

In this use case, the objective is to create leads with attachments using Python and Flask, making use of Salesforce’s REST API functionality.

For the purpose of writing a blog, this line succinctly captures the essence of the task at hand: ‘We can leverage the Simple Salesforce API to seamlessly integrate and create leads with attachments, utilizing the robust functionality of Salesforce’s REST API in Python with Flask.

Step (1):  Create a connected app in salesforce

Create a connected app for authentication and authorization, enabling the use of a password with a security token. To obtain the client ID and client secret, simply create a connected app in Salesforce. It’s a straightforward process—just create and allow REST API access; that’s it.

Step (2): Now, install Python on your local machine and use the PyCharm Community Edition editor for running Python locally. 

(i) Import Salesforce SimpleAPI.

(ii) Also, import Flask for the frontend web application.

(iii) Import all the libraries by simply clicking the code and importing; this allows direct importing from the editor.

(iv) Change the username, password, client ID, and client secret on your own. For best practices, you can separate these configurations somewhere else. This is a demo org, which is why I am providing this for understanding purposes.

(v) Create a “templates” folder; the name should be exactly “templates” only. Then, add an HTML file inside and link it with the main Python file, which I am currently using for

import json
import os
from flask import Flask, render_template, request, redirect, url_for, jsonify
import base64
import requests

#make sure to import all the packages first

params = {
    "grant_type": "password",
    "client_id": "Your clientId",
    "client_secret": "Your clientId",
    "username": "yourUserName",
    "password": "password + securityToken" #without any space 
r ="", params=params)
access_token = r.json().get("access_token")
instance_url = r.json().get("instance_url")
print("Access Token:", access_token)
print("Instance URL", instance_url)

def sf_api_call(action, parameters={}, method='get', data={}):
    headers = {
        'Content-type': 'application/json',
        'Accept-Encoding': 'gzip',
        'Authorization': 'Bearer %s' % access_token
    if method == 'get':
        r = requests.request(method, instance_url + action, headers=headers, params=parameters, timeout=30)
    elif method in ['post', 'patch']:
        r = requests.request(method, instance_url + action, headers=headers, json=data, params=parameters, timeout=10)
        raise ValueError('Method should be get or post or patch.')
    print('Debug: API %s call: %s' % (method, r.url))
    if r.status_code < 300:
        if method == 'patch':
            return None
            return r.json()
        raise Exception('API error when calling %s : %s' % (r.url, r.content))

app = Flask(__name__)

def index():
    return render_template('LeadCreateWithAttachment.html')

@app.route('/submit', methods=['POST'])
def submit():
    # Get form data from the request
    first_name = request.form['firstName']
    last_name = request.form['lastName']
    company = request.form['company']

    # Salesforce REST API endpoint for creating a Lead
    api_endpoint = f"{instance_url}/services/data/v60.0/sobjects/Lead/"

    # Prepare data for the Lead object
    lead_data = {
        'FirstName': first_name,
        'LastName': last_name,
        'Company': company

    # Prepare headers with authorization token
    headers = {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type': 'application/json'
    response =, headers=headers, json=lead_data)

    print("Lead Creation Response:", response.json())  # Add this line for debugging

    if response.status_code == 201:
        # return upload_file(lead_id)
        lead_id = response.json().get('id')
        return 'Lead created successfully with attachments'
        # Error creating Lead
        return jsonify({'error': 'Failed to create Lead'})

@app.route('/upload', methods=['POST'])
def upload_file(lead_id):
    file = request.files['file']
    if file:
        encoded_string = base64.b64encode("utf-8")
        content_version = sf_api_call('/services/data/v60.0/sobjects/ContentVersion', method="post", data={
            'Title': file.filename,
            'PathOnClient': file.filename,
            'VersionData': encoded_string,
        content_version_id = content_version.get('id')
        content_version = sf_api_call(f'/services/data/v60.0/sobjects/ContentVersion/{content_version_id}')
        content_document_id = content_version.get('ContentDocumentId')

        # Create a ContentDocumentLink
        content_document_link = sf_api_call('/services/data/v60.0/sobjects/ContentDocumentLink', method='post', data={
            'ContentDocumentId': content_document_id,
            'LinkedEntityId': lead_id,
            'ShareType': 'V'

        return f'Successfully uploaded {file.filename} and linked to record with Id {lead_id}!'
        return 'No file uploaded.'

if __name__ == '__main__':


<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Salesforce Lead Form</title>
        body {
            text-align: center;
            background-color: #f4f4f4; /* Light gray background */
        form {
            display: inline-block;
            text-align: left;
            background-color: #fff; /* White background */
            padding: 20px;
            border: 1px solid #ccc; /* Light gray border */
            border-radius: 8px; /* Rounded corners */
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Box shadow for a subtle effect */
        table {
            width: 100%;
        table, th, td {
            border-collapse: collapse;
        th, td {
            padding: 10px;
            border: 1px solid #ddd; /* Light gray border for table cells */
        th {
            background-color: #f2f2f2; /* Light gray background for table header */
        button {
            background-color: #4caf50; /* Green button color */
            color: #fff; /* White text color */
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;


    <h2>Create Lead</h2>

    <form method="post" action="{{ url_for('submit') }}" enctype="multipart/form-data">
                <td><label for="firstName">First Name:</label></td>
                <td><input type="text" id="firstName" name="firstName" required></td>
                <td><label for="lastName">Last Name:</label></td>
                <td><input type="text" id="lastName" name="lastName" required></td>
                <td><label for="company">Company:</label></td>
                <td><input type="text" id="company" name="company" required></td>

                <td><label for="file">Select a PDF file:</label></td>
                <td><input type="file" id="file" name="file" accept=".pdf" required></td>
                <td colspan="2" style="text-align: center;"><button type="submit">Submit</button></td>