Skip to main content
No items found.
logo dropboxsign
Why Dropbox Sign?
Expand or collapse accordion

What you can do

Sign documents online
Create electronic signatures
Choose or create templates
Fill and sign PDFs
Complete online contracts
Document management
Explore features
icon arrow right

Use cases

Sales and business development
Human resources
Startups
Financial technology
Real estate
On-demand services
Products
Expand or collapse accordion
icon dropbox
Sign
Make it easy to send and sign
icon dropbox
Sign API
Integrate eSign in your workflow
icon dropbox fax
Fax
Send faxes without a fax machine
icon dropbox integrations
Integrations
We meet you where you work
Resources
Expand or collapse accordion
Blog
Workflow expertise & product news
Customer stories
Real-world stories with real results
Help center
In-depth guidance for our products
Resource library
Reports, videos, and info sheets
Developers
Pricing
Expand or collapse accordion
Dropbox Sign pricing
Find the right plan for you
Dropbox Sign API pricing
Real-world stories with real results
Contact sales
Sign up
Contact Sales
Sign in
Expand or collapse accordion
Dropbox Sign
Dropbox Forms
Dropbox Fax
Free trial
Blog
/
Developers

Getting started with Dropbox Sign Embedded Signing using Python

by 
Kevin Kimani
November 11, 2024
22
minute read
icon tooltip

New look, same great product! HelloSign is now Dropbox Sign.

icon close

Dropbox sign has two options for signing: embedded signing and non-embedded signing. Dropbox Sign embedded signing allows users to legally sign documents directly on your website or mobile application rather than being redirected to a third-party site. In contrast, Dropbox Sign non-embedded signing directs users to Dropbox Sign's platform to complete the signing process, taking them outside of your application. In this article, we'll be focusing on Dropbox Sign embedded signing which works by providing a comprehensive API alongside a client-side library that adds the embedded signing experience to your site via an iFrame.

‍

For example, if you're trying to onboard a cohort of new employees, Dropbox Sign embedded signing makes this process easy. New hires can easily access and sign essential documents, such as employee contracts, directly through your company's onboarding portal. This approach eliminates physical paperwork and ensures that all the necessary forms are completed accurately and securely.

‍

In this article, you'll learn how to leverage Dropbox Sign to integrate the embedded signing experience into a Python application where you can upload documents that need to be signed. You'll also see how the signatories can access and sign these documents through the same site.

‍

Implementing Dropbox Sign Embedded Signing in Python

‍

To follow along with this guide, make sure you have the following:

  • A Dropbox Sign account
  • A Python 3 environment set up on your local machine
  • The Git CLI installed on your local machine
  • A code editor, such as Visual Studio Code, and a web browser, such as Google Chrome

Once you have all these prerequisites, it's time to generate an API key and create an API app on the Dropbox Sign dashboard.

‍

Generating an API key and creating an API app

On the Dropbox Sign dashboard, you need to generate an API key and a client ID that will enable communication between your application and the Dropbox Sign API.

‍

To obtain the credentials, log into your Dropbox Sign account, navigate to the Settings page, and select the API tab and then the Generate key:

‍

API tab

‍

On the New key details modal, input "Python Embedded Signing" as the key's name and click Generate key. Take note of the key value, which you'll need later.

On the same page, click the Create app button to create a new API app and generate a client ID:

‍

Create API app

‍

On the Create a new API app page, provide the application name (*eg* "PythonSign") and set the domain (*eg* example.com). This information is necessary for the production environment only and does not affect your local development in any way. Once you've provided this information, scroll down the page and click CREATE APPLICATION:

‍

Providing the API app details

‍

This creates the API application and generates a client ID. Again, take note of the API app value as you'll need it later.

‍

Preparing the server

To make it easy for you to follow along and focus on integrating the embedded signing experience, a starter template has been created in Flask. This template contains all the authentication logic implemented using Flask-Login, the model definitions, and the necessary frontend code.

‍

For simplicity, this project uses a SQLite database.

‍

To clone the starter template to your local machine, execute the following command:

```bash
git clone --single-branch -b starter-template https://github.com/kimanikevin254/dropboxsign-python-demo.git
```

‍

Navigate into the project folder, set up a virtual environment, activate it, and install all the necessary dependencies using the following commands:


```bash
cd dropboxsign-python-demo

python3 -m venv venv

source venv/bin/activate

pip install -r requirements.txt
```

‍

Next, execute this command to copy the contents of the .env.example file into a new file named .env:


```bash
cp .env.example .env
```

‍

This new .env file holds all the environment variables. In it, replace the placeholder values for the DS_API_KEY and DS_CLIENT_ID with the API key and client ID values you obtained in the previous section. For SECRET_KEY, you can use any value.

‍

You can find all the model definitions in the app/models file. The User model represents a user of the application, the SignatureRequest model represents a request for signatures on a document, and the Signatory model represents a signatory for the signature request. In the SignatureRequest model, the reference_id field stores the value of the document identifier in the Dropbox Sign servers. In the Signatory model, the ds_signature_id field stores the ID associated with the signatory on Dropbox Sign servers.

‍

To run the application, set the FLASK_APP and FLASK_DEBUG environment variables by executing the following commands in your terminal:


```bash
export FLASK_APP=app
export FLASK_DEBUG=1
```

‍

Then, run the Flask server:


```bash
flask run
```

‍

This creates a SQLite database with all the defined tables in the `instance` folder.

‍

Navigate to http://localhost:5000 on your browser, and you'll be prompted to create an account and log into the application. After successful authentication, you are redirected to the dashboard page. Later on, after implementing the embedded signing experience, the dashboard will display all the embedded signature requests. You'll also be able to create signature requests, view their details, and sign the documents.

‍

Integrating Dropbox Sign API into the application

To integrate the embedded signing experience, there are a couple of steps you need to complete:

‍

1. Create an embedded signature request

2. Generate a Sign URL for the signers

3. Complete the signature request by signing the document

‍

Initially, you need to install the Dropbox Sign Python SDK. Make sure that the virtual environment is still activated and then execute the following command:


```bash
pip install dropbox-sign
```

‍

Once the Dropbox Sign Python SDK is installed, you can create an embedded signature request.

‍

Creating an Embedded Signature request

In this tutorial, you'll create the embedded signature request after a user submits the form defined in the app/templates/signatureRequests/create.html file. This form requires a user to provide the title and description of the signature request, the document to be signed, and the details of the signers.

‍

To implement the functionality required to allow the user to create an embedded signature request, open the app/signature_requests.py file and replace the create method with the following code:


```py
def create():
   if request.method == 'GET':
       return render_template('signatureRequests/create.html')
  
   if request.method == 'POST':
       # Extract data from the request body
       title = request.form.get('title')
       description = request.form.get('description')
       signatory_names = request.form.getlist('signatory_name[]')
       signatory_emails = request.form.getlist('signatory_email[]')
       signatory_positions = request.form.getlist('signatory_position[]')

       # Save the document to file system
       if 'document' not in request.files:
           return 'Document is required'

       file = request.files['document']

       if file.filename == '':
           return 'No selected file'

       if file and allowed_file(file.filename):
           filename = secure_filename(file.filename)
           upload_folder = current_app.config['UPLOAD_FOLDER']

           # Create the directory if it doesn't exist
           if not os.path.exists(upload_folder):
               os.makedirs(upload_folder)

           # Generate a unique filename using timestamp and uuid
           timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
           random_chars = str(uuid.uuid4())[:8]
           unique_filename = f"{timestamp}_{random_chars}_{filename}"

           file.save(os.path.join(upload_folder, unique_filename))

           # Save the signature request details to the database
           new_signature_request = SignatureRequest(
               title=title,
               description=description,
               document_url=unique_filename,
               initiator_id=current_user.user_id,
               status='pending',
               reference_id="" # Will be updated later
           )

           db.session.add(new_signature_request)
           db.session.commit()

           # Save the signatories to DB and create signers list
           signers = []

           for name, email, position in zip(signatory_names, signatory_emails, signatory_positions):
               new_signatory = Signatory(
                   name=name,
                   email=email,
                   position=position,
                   signature_request_id=new_signature_request.request_id,
                   status='pending',
                   ds_signature_id="" # Will be updated later
               )

               db.session.add(new_signatory)

               # Append to signers list
               signer = {
                   'name': name,
                   'email': email
               }
               signers.append(signer)

           db.session.commit()

           #  Create embedded signature request on Dropbox Sign
           response = create_embedded_signature_request(
               title=title,
               description=description,
               signers=signers,
               filepath=os.path.join(upload_folder, unique_filename)
           )

           # Update the signature request reference ID in the DB
           new_signature_request.reference_id = response.to_dict()['signature_request']['signature_request_id']
           db.session.commit()

           # Update the signatories ds_signature_id
           signatures = response.to_dict()['signature_request']['signatures']

           for signer in signers:
               for signature in signatures:
                   if signature['signer_email_address'] == signer['email'] and signature['signer_name'] == signer['name']:
                       # Update ds_signature_id for matching signatory
                       db.session.query(Signatory)
                           .filter(Signatory.email == signer['email'], Signatory.name == signer['name'])
                           .update({"ds_signature_id": signature['signature_id']})

           db.session.commit()

           # Redirect home
           return redirect(url_for('signature_requests.index'))
```

‍

This code extracts data from the request body, including the title, description, and lists of signatory names, emails, and positions. It then saves the uploaded document to the specified upload folder (uploads, defined in the __init__.py file) with a unique file name generated using timestamps and UUIDs to prevent naming conflicts. The signature request details and the uploaded document's file name are then saved to the database using SQLAlchemy. For each signatory, a new Signatory object is created and added to the database.

‍

After committing these changes to the database, the code proceeds to initiate an embedded signature request using the create_embedded_signature_request function (you'll implement this later), which interfaces with the Dropbox Sign API. The response from this function is used to update the signature request reference ID and link each signatory's unique signature ID (ds_signature_id).

‍

Next, you need to implement the create_embedded_signature_request function. Open the app/utils.py file and add the following import statement to the file:


```py
from dropbox_sign import ApiClient, ApiException, Configuration, apis, models
```

‍

Then, create a configuration instance to be used in subsequent API calls by adding the following code just below the import statements:


```py
configuration = Configuration( username = os.getenv('DS_API_KEY') )
```

‍

Replace the create_embedded_signature_request function with the following code:


```py
def create_embedded_signature_request(title, description, signers, filepath):
   with ApiClient(configuration=configuration) as api_client:
       signature_request_api = apis.SignatureRequestApi(api_client=api_client)

       signing_options = models.SubSigningOptions(
           draw=True,
           type=True,
           upload=True,
           phone=True,
           default_type="draw",
       )

       # Create signers_list using list comprehension
       signers_list = [
           models.SubSignatureRequestSigner(
               email_address=signer.get('email'),
               name=signer.get('name'),
               order=index
           )
           for index, signer in enumerate(signers)
       ]

       data = models.SignatureRequestCreateEmbeddedRequest(
           client_id = os.getenv('DS_CLIENT_ID'),
           title = title,
           subject = title,
           message = description,
           signers = signers_list,
           cc_email_addresses = ["lawyer@example.com"],
           files = [open(file=filepath, mode="rb")],
           signing_options = signing_options,
           test_mode=True
       )

       try:
           return signature_request_api.signature_request_create_embedded(data)
       except ApiException as e:
           print("Exception when calling Dropbox Sign API: %s\n" % e)
```

‍

This code configures an ApiClient instance using the configuration object you set up in the previous step. Within the API client context, it initializes a SignatureRequestApi object to manage signature requests. It then defines signing options (signing_options), such as drawing, typing, uploading, and phone-signing capabilities, with a default type set to draw. It constructs a list of signers (signers_list) using a list comprehension, iterating through a list of signatory data to create SubSignatureRequestSigner objects containing email addresses, names, and signing orders. The signing order ensures that the signatory with order 1 cannot sign the document before the signatory with order 0.

The data needed for the embedded signature request is encapsulated in [SignatureRequestCreateEmbeddedRequest], including client ID, title, description, signers list, carbon copy email addresses, uploaded files, signing options, and test mode enabled. Finally, it attempts to create the embedded signature request using the signature_request_create_embedded method, handling any potential ApiException errors that may occur during the API call.

‍

Generating a Sign URL for signers

Now that you've created an embedded signature request, it's time to implement the functionality that enables the signatories to sign the documents. The signatories can sign the document from the signature request details page defined in the app/templates/signatureRequests/details.html file.

‍

Before displaying the signature request details, you need to generate a Sign URL of the current user listed as a signatory in the database for the specified document. Open the app/signature_requests.py file and replace the view function with the following code:


```py
def view(request_id):
   # Retrieve the signature request from the database
   signature_request = SignatureRequest.query.filter_by(request_id=request_id).first()

   # If signature request does not exist
   if not signature_request:
       abort(404)

   # Check if the current user is authorized to view this request
   if (signature_request.initiator_id != current_user.user_id and
           not any(signatory.email == current_user.email for signatory in signature_request.signatories)):
       abort(403)

   # If the current user is a signatory, generate a signing URL
   sign_url = None
   for signatory in signature_request.signatories:
       if signatory.email == current_user.email:
           response = generate_sign_url(signatory.ds_signature_id)
           sign_url = response.to_dict()['embedded']['sign_url']
           break

   return render_template('signatureRequests/details.html', signature_request=signature_request, sign_url=sign_url)
```

‍

This code retrieves a specific signature request from the database using the request_id extracted from the URL. If the signature request does not exist, it aborts with a 404 error. It then checks if the current user is authorized to view the request. If the user is neither the initiator nor a signatory, it aborts with a 403 error. If the current user is a signatory, it generates a signing URL for the user by calling the generate_sign_url function with the signatory's signature ID. You'll implement this function in the next step. Finally, it renders the signatureRequests/details.html template, passing the signature request details and the signing URL to the template.

‍

Next, open the app/utils.py file and replace the generate_sign_url function with the following code:


```py
def generate_sign_url(signature_id):
   with ApiClient(configuration=configuration) as api_client:
       embedded_api = apis.EmbeddedApi(api_client=api_client)

       try:
           return embedded_api.embedded_sign_url(signature_id=signature_id)
       except ApiException as e:
           print("Exception when calling Dropbox Sign API: %s\n" % e)
```

‍

This code creates an ApiClient instance with the configuration you defined in the previous section and initializes an EmbeddedApi object to manage embedded signing operations. Within this context, it attempts to retrieve an embedded signing URL using the embedded_sign_url method, passing in the signatory's signature_id. If an ApiException occurs during the API call, it catches the exception and prints an error message.

‍

Completing the signature request by signing the document

‍

At this point, you've implemented the functionality to create a signature request and generated a Sign URL for the signatories. Now, you need to work on the user interface to allow the signatories to sign the document. You do this by modifying the signature request details page.

‍

Open the app/templates/signatureRequests/details.html file and add the following code just before the block end tag ({% endblock %}):


```html
<script src="https://cdn.hellosign.com/public/js/embedded/v2.11.1/embedded.development.js"></script>

<script>
   const client = new HelloSign();

   function signDoc() {
       client.open("{{ sign_url | safe }}", {
           clientId: "<YOUR-CLIENT-ID>",
           skipDomainVerification: true,
           testMode: true,
       });
   }
</script>
```

‍

This code loads the Dropbox Sign client-side library]by linking to the CDN and defines a function, signDoc, that uses this library to open a signing window. When the signDoc function is called, it leverages a HelloSign client instance to open a signing URL (retrieved from a Jinja template variable sign_url) in an iFrame. It passes configuration options (such as the client ID), skips domain verification, and enables test mode.

‍

> Remember to replace `<YOUR-CLIENT-ID>` with the API app client ID you obtained earlier.

‍

With this, the signatories can now sign the documents. The client-side library emits various events that you can listen to, and then you can take the appropriate action. For example, you can listen when a signatory signs a document and then update their status in the database from pending to completed.

‍

Testing the Embedded Signing experience

‍

Congratulations! You've completed the Dropbox Sign API integration. But before you can test the application, you need to make a few more updates.

‍

When a user opens the dashboard, you need to display all signature requests where they are either the initiator or the signatory. To do this, open the app/signature_requests.py and replace the index method with the following code:


```py
def index():
	# Query signature requests where the current user is the initiator
	sent_requests = SignatureRequest.query.filter_by(initiator_id=current_user.user_id).all()
    
	# Query signature requests where the current user is a signatory
	received_requests = SignatureRequest.query.filter(SignatureRequest.signatories.any(Signatory.email == current_user.email)).all()
    
	return render_template('signatureRequests/index.html', sent_requests=sent_requests, received_requests=received_requests)
```

‍

This code retrieves all signature requests from the database where the current user is either the initiator or the signatory. It uses a query to filter SignatureRequest records, checking if the initiator_id matches the current user's ID or if any of the associated signatories have an email matching the current user's email. The filtered results are stored in the all_requests variable, which is then passed to the index.html template for rendering.

‍

Next, on the signature request details page, you need to display the document that is being signed. Currently, the frontend includes the logic to display the file, but the backend cannot serve the document. To do this, open the app/signature_requests.py file and replace the uploaded_file function with the following code:


```py
def uploaded_file(filename):
   # Get the base directory of the application
   basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))

   # Combine the base directory with the relative upload folder
   upload_folder = os.path.join(basedir, current_app.config['UPLOAD_FOLDER'])

   # Construct the full file path
   filepath = os.path.join(upload_folder, filename)

   # Check if the file exists
   if not os.path.isfile(filepath):
       abort(404, description="File not found")

   # Send the file
   return send_from_directory(directory=upload_folder, path=filename)
```

‍

This code determines the application's base directory. It then constructs the full path to the file by combining this base directory with the upload folder specified in the application's configuration. If the file does not exist at the constructed path, a 404 error is returned. If the file does exist, it's sent to the browser using the send_from_directory function.

‍

Now, it's time to test the application. Make sure the server is running by executing the command flask run in the terminal. Then navigate to http://localhost:5000/create. Fill out all the details required in the form and click Create Signature Request:

Creating a signature request

‍

Once the signature request is created, you are redirected to the dashboard, where it's displayed under the Sent Requests tab. Make sure you've signed in with the first signatory's email address and you'll see the signature request under the Received Requests tab. 

‍

Click the View action on the signature request to open the details page. You should see the details of the signature request as well as a button to start the signing process:

Signature request details

‍

Click the Sign Document button to open the signing interface. An alert is displayed to inform you that this signature is not legally binding. This happens in the development environment (remember, you set the signature request in test_mode) only since your API client app has not yet been approved by Dropbox Sign:

The signing interface showing a disclaimer

‍

Dismiss the alert, scroll down to the bottom of the document, and sign it:

‍

Signed document

‍

This confirms that the integration is working as expected.

For reference purposes, the full code for this application is available on GitHub.

‍

Making the solution production-ready

‍

At this point, the embedded signing experience works as expected in the development environment, but isn't optimized for production. Before you can push your application to the production environment, consider the following:

  • Ensure the domain name you provide during API app creation is correct as embedded flows work only on the specified domain and its subdomains. Providing the top-level domain makes the embedded signing experience available on your subdomains as well.
  • Get your API app approved. When signing the document, there is an alert informing the user that the signature they provide is not legally binding. To avoid this in production, ensure your app is approved to comply with eSignature regulatory policies.
  • Listen to the client-side library events or the webhook events sent to your callback URLs and perform the necessary actions. For example, once a document is signed, you can update the status in the database.

‍

Conclusion

In this guide, you learned how to use Dropbox Sign to implement an embedded signing experience into your Python application. You also explored steps to make sure that your application is production-ready.

Dropbox Sign offers a seamless and secure way to handle eSignatures. It simplifies the document-signing process, enhances document workflows, and ensures compliance with industry standards. Try Dropbox Sign today!

‍

Stay in the loop

Done! Please check your inbox.

Thank you!
Thank you for subscribing!

Lorem ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum
icon arrow right
icon close

Up next:

Close-up illustration of a handwritten signature, representing modern digital signing solutions.
Developers
15
minute read

Integrating Dropbox Sign with Ruby on Rails: A Step-by-Step Tutorial

Close-up illustration of a handwritten signature, representing modern digital signing solutions.
Developers
15
minute read

Dropbox Sign vs. SignNow for developers

Info sheet

eSignatures: BUILD or BUY?

Products
Dropbox SignDropbox Sign APIDropbox FaxIntegrations
Why Dropbox Sign
Electronic signaturesSign documentsSign and Fill PDFsOnline contractsCreate electronic signaturesSignature editorSign word documents
Support
Help centerContact salesContact supportManage cookiesGetting started: Dropbox SignGetting started: Dropbox Sign API
Resources
BlogCustomer storiesResource centerLegality guideTrust center
Partners
Strategic PartnersPartners locator
Company
CareersTermsPrivacy
icon facebookicon youtube

Accepted payment methods

Mastercard logoVisa logoAmerican Express LogoDiscover logo
CPA Compliance BadgeHIPAA compliance badgeSky High Enterprise Ready badgeISO 9001 Certified badge

Dropbox Sign electronic signatures are legally binding in the United States, European Union, United Kingdom, and in many countries around the world.
For more information, please view our Terms and Conditions and Privacy Policy