Signing an individual file

  1. Start the Python shell and configure its session.

    The following variables should be available now:

    >>> base_url  # the base URL of the API
    'https://eu2-cloud.acronis.com/api/notary/v2'
    >>> auth  # the 'Authorization' header value with the access token
    {'Authorization': 'Bearer 8770b34b74f9e4d9424eff50c38182bb4ae7f5596582ae61900b1b6a23e3ec58'}
    
  2. Define a variable named file_path, and then assign the path to your file to this variable:

    >>> file_path = '<path to file>'
    

    Important

    If the path contains backslashes, remember to escape them with an additional backslash.

  3. Open the file for reading in binary mode:

    >>> file = open(file_path, 'rb')
    
  4. Define a variable named files, and then assign an object containing the file in the file key to this variable:

    >>> files = {
    ...     'file': file
    ... }
    
  5. [Optional] To sign the file with embedded signatures of the signees, create a variable named file_data and assign it with an object containing the embedded key with its value to true:

    >>> file_data['embedded'] = True
    

    Note

    In order to sign the file with embedded signatures, it must comply with the following requirements:

    1. The file must be either in PDF or in one of the following file types that will be converted to PDF by the notary service:

      • .doc/.docx

      • .xls/.xlsx

      • .ppt/.pptx

      • .txt

    2. Maximum file size limit is 32 MB.

    3. The file must not be protected by password.

    4. If the uploaded file is in PDF, “Changing the Document” and “Commenting” permissions must be set to “Allowed”.

  6. Upload the file to the notary storage for signing by sending a POST request to the /stored-files/sign endpoint:

    >>> response = requests.post(f'{base_url}/stored-files/sign', headers=auth, files=files, data=file_data)
    
  7. Close the file:

    >>> file.close()
    
  8. Check the status code of the response:

    >>> response.status_code
    201
    

    Status code 201 means that the notary service has received the file, saved it to the storage, and an e-sign document containing the signing state has been created.

    A different status code means that an error has occurred. For the details, refer to “Status and error codes”.

    Also, the response body contains an object containing the ID of the stored file formatted as a JSON text. When converted to an object, it will look as follows:

    >>> pprint.pprint(response.json())
    {
        "file_id": "aa95bac7-33fd-4faa-9fa1-f4baeaca82cd"
    }
    
  9. Fetch the uploaded file ID that can be used to fetch the file information, download the file, or delete it:

    >>> file_id = response.json()['file_id']
    >>> file_id
    'aa95bac7-33fd-4faa-9fa1-f4baeaca82cd'
    
  10. Fetch the details about the file by sending a GET request to the /stored-files/{file_id} endpoint:

    >>> response = requests.get(f'{base_url}/stored-files/{file_id}', headers=auth)
    
  11. Check the status code of the response:

    >>> response.status_code
    200
    

    Status code 200 means that the request was successful.

    A different status code means that an error has occurred. For the details, refer to “Status and error codes”.

    Also, the response body contains the file key containing the details about the file formatted as a JSON text. When converted to an object, it will look as follows:

    >>> pprint.pprint(response.json())
    {'file': {..., 'certificate': {...},
              'document_id': '8592b052a9979f8d651618092bb3ac8472beebc66d52ab980deb4d320e26b0cb'}}
    
  12. Convert the JSON text that the response body contains to an object, and then fetch the ID of the e-sign document:

    >>> doc_id = response.json()['file']['document_id']
    >>> doc_id
    '8592b052a9979f8d651618092bb3ac8472beebc66d52ab980deb4d320e26b0cb'
    
  13. Define a variable named signees, and then assign an object containing the list of signees’ email addresses in the emails key to this variable:

    >>> signees = {
    ...     'emails': [
    ...         'john.smith@example.com',
    ...         'john.doe@example.com'
    ...     ]
    ... }
    

    If you want to sign this file, include your email address in this list.

    Warning

    It will not be possible to change the list of signees after sending the initial list to the notary service.

  14. [Optional] If you chose to embed the signee signatures in the file:

    1. Follow the Generating PDF annotations procedure to fetch the list of PDF annotations.

    2. Add the annotations key and assign it with the list of PDF annotations:

      >>> signees['annotations'] = [
      ...     "{\"bbox\":[129.81423950195312,216.01904296875,200,50], ...}",
      ...     "{\"bbox\":[186.60797119140625,409.7261962890625,200,50], ...}"
      ... ]
      
  15. Convert the signees object to a JSON text:

    >>> signees = json.dumps(signees, indent=4)
    >>> print(signees)
    {
        "emails": [
            "john.smith@example.com",
            "john.doe@example.com"
        ]
    }
    
  16. Send a POST request with the JSON text to the /documents/{doc_id}/signees endpoint:

    >>> response = requests.post(
    ...     f'{base_url}/documents/{doc_id}/signees',
    ...     headers={'Content-Type': 'application/json', **auth},
    ...     data=signees,
    ... )
    
  17. Check the status code of the response:

    >>> response.status_code
    200
    

    Status code 200 means that the notary service has initiated the signing and sent email messages with the signature request to the signees. To check the signing status, refer to Checking the status of signing process.

    A different status code means that an error has occurred. For the details, refer to “Status and error codes”.

Full code example

 1#!/usr/bin/env python3
 2
 3import requests  # Will be used for sending requests to the API.
 4import hashlib   # Will be used for calculating hash values.
 5import os.path   # Will be used for path-related operations.
 6import pprint    # Will be used for formatting the output of JSON objects received in API responses.
 7import json      # Will be used for converting dictionaries into JSON text
 8
 9# Define variables named "LOGIN" and "PASSWORD" and then assign them with your account credentials
10LOGIN = '<your login>'        # Change login here
11PASSWORD = '<your password>'  # Change password here
12
13# Define a variable named "cloud_url" and then assign it with the URL of the cloud platform
14cloud_url = 'https://cloud.acronis.com'
15
16# Fetch the URL of the data center where your account is located by sending a GET request to the "/api/1/accounts" endpoint
17response = requests.get(
18    f'{cloud_url}/api/1/accounts',
19    params={'login': LOGIN}
20)
21response.raise_for_status()
22
23# Convert the JSON text that the response body contains to a dictionary and store the data center URL
24# in a variable that will be used in further requests
25server_url = response.json()['server_url']
26
27# Define a variable named "account_creds", and then assign the username and password to this variable
28account_creds = {
29    'username': LOGIN,
30    'password': PASSWORD
31}
32
33# Generate a token by sending a POST request to the "/api/2/idp/token" with your account credentials to the cloud platform
34response = requests.post(
35    f'{server_url}/api/2/idp/token',
36    headers={'Content-Type': 'application/x-www-form-urlencoded'},
37    data={'grant_type': 'password', **account_creds}
38)
39response.raise_for_status()
40
41# Convert the JSON text that the response body contains to a dictionary and then assign it to a variable named "token_info"
42token_info = response.json()
43
44# Define a variable named "auth" and then assign it with a dictionary with "Authorization" key containing
45# token string formatted as "Bearer <access_token>"
46auth = {
47    'Authorization': 'Bearer ' + token_info['access_token']
48}
49
50# Define a variable named "base_url", and then assign the API base URL using the data center URL
51# to this variable
52base_url = f'{server_url}/api/notary/v2'
53
54# Define a variable named "file_id" and then assign it with the ID of the file
55file_path = '<path to file>'  # Change path to file here
56
57# Open the file for reading in binary mode
58file = open(file_path, 'rb')
59
60# Define a variable named "files" and then assign it with the dictionary with "file" key containing a file
61files = {
62    'file': file
63}
64
65# Upload the file to the notary storage for signing by sending a POST request to the "/stored-files/sign" endpoint
66response = requests.post(f'{base_url}/stored-files/sign', headers=auth, files=files)
67response.raise_for_status()
68
69# Close the file
70file.close()
71
72# Convert the JSON text that the response body contains to a dictionary and fetch the ID of the file
73file_id = response.json()['file_id']
74
75# Fetch the information about the uploaded file by sending a GET request to the "/stored-files/{file_id}" endpoint
76response = requests.get(f'{base_url}/stored-files/{file_id}', headers=auth)
77response.raise_for_status()
78
79# Convert the JSON text that the response body contains to a dictionary and fetch the ID of the document
80doc_id = response.json()['file']['document_id']
81
82# Define a variable named "signees"
83# and then assign it with a dictionary with "emails" key containing an array of the emails of the signees
84signees = {
85    'emails': ['john.smith@example.com', 'john.doe@example.com']
86}
87
88# Convert the "signees" dictionary to a JSON text
89signees = json.dumps(signees, indent=4)
90
91# Send the list of signees to the notary service by sending a POST request to the "/documents/{doc_id}/signees" endpoint
92response = requests.post(
93    f'{base_url}/documents/{doc_id}/signees',
94    headers={'Content-Type': 'application/json', **auth},
95    data=signees,
96)
97response.raise_for_status()