Creating and managing e-signs

Use the API to create and manage e-signs.

  1. Create new e-sign
  2. Working with multiple documents
  3. Different authentication types
  4. Options for signing order
  5. Working with e-sign after activation

1. Create new e-sign for signing

E-sign needs to have contract to be signed and possible attachments. Typical way to get started is to create contract with document to be signed.

Next we can create e-sign which will sign this given contract. You need to have ID from the new contract.

You can add e-sign parties within e-sign's create request, but adding those one by one in separate requests is also possible.

import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
CONTRACT_ID = "ct_1JrjJRTzEMlHVHls"
payload = {
    "contract": CONTRACT_ID,
    "parties": [
        {
            "name": "John Doe",
            "email": "john.doe@mycompany.com",
            "role": "signer",
            "authentication_type": "email",
        },
        {
            "name": "Jane Doe",
            "email": "jane.doe@customer.com",
            "role": "signer",
            "authentication_type": "email",
        }
    ]
}

response = requests.post("https://sandbox.zefort.com/api/esigns/", 
                         auth=(ZEFORT_APIKEY, ""),
                         json=payload)

print(response.json())
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."

curl -X POST "https://sandbox.zefort.com/api/esigns/" \
     -u $ZEFORT_APIKEY: \
     -H "Content-Type: application/json" \
     -d '{"contract": "ct_1JrjJRTzEMlHVHls", "parties": [{"name": "John Doe", "email": "john.doe@mycompany.com", "role": "signer", "authentication_type": "email"},{"name": "Jane Doe", "email": "jane.doe@customer.com", "role": "signer", "authentication_type": "email"}]}'


Response:

{
    "id": "esgn_lHVHls1JrjJRTzEM",
    "owner": {...},
    "contract": "ct_1JrjJRTzEMlHVHls",
    "contract_binders": [],
    "title": "",
    "sign_order": "parallel",
    "greetings": null,
    "need_auth_to_view_documents": false,
    "signed": false,
    "status": "draft",
    "activated": false,
    ...
    "parties": [
        {
            "id": "sgnr_JrjJRTzEMlHVHls1",
            "name": "John Doe",
            "additional_info": null,
            "email": "john.doe@mycompany.com",
            "phone_number": null,
            "language": "en-us",
            "personal_id": null,
            "status": "draft",
            "role": "signer",
            "ordinal": 2,
            "authentication_type": "email",
            "need_auth_to_view_documents": false,
            ...
        },
        ...
    ],
    "documents": [
        {
            "id": "esdc_1KgKFkdqWRU7keVFq5",
            "content_type": "application/pdf",
            "filename": "test.pdf",
            "signable": true,
            "needs_to_be_viewed": true
        }
    ]
}

When e-sign parties are set with e-mail, both email and name and mandatory fields. E-sign party can also be set with user field, which value will be ID of user. In this case name and email will be populated from user's data.

After e-sign has been created, parties cannot be anymore added or updated along e-sign data. Instead own endpoints to e-sign parties should be used.

After e-sign has been initialized, it can be activated for signing. Activate request will send e-mails to parties whose signing or approval is pending.

import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"

response = requests.post(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/activate/", 
    auth=(ZEFORT_APIKEY, "")
)
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
ESIGN_ID="esgn_lHVHls1JrjJRTzEM"

curl -X POST "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/activate/" \
     -u $ZEFORT_APIKEY:


After activation signing process proceeds automatically.

2. Working with multiple documents

Zefort Sign can have only one document which will be signed, but attachments can be added to e-sign process. Easiest way to do this is to add multiple documents to given contract. Main document will be signed and other documents will be added as attachments.

It's possible to mix documents from different contracts, if you e.g. have document of general terms and conditions, you can always use same document as an attachment. In this case you need to specify contract, document and attachments fields to e-sign. Document, which will be signed, needs to belong to given contract.

import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
CONTRACT_ID = "ct_1JrjJRTzEMlHVHls"
DOCUMENT_ID = "doc_2VHlsJrjJRTzEMlH"
ATTACHMENT1_ID = "doc_5zEMlHVHlsJrjJRT"
ATTACHMENT2_ID = "doc_7RTzEMlHVHlsJrjJ"
payload = {
    "contract": CONTRACT_ID,
    "document": DOCUMENT_ID,
    "attachemnts": [ATTACHMENT1_ID, ATTACHMENT2_ID]
}

response = requests.post("https://sandbox.zefort.com/api/esigns/", 
                         auth=(ZEFORT_APIKEY, ""),
                         json=payload)

print(response.json())
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
curl -X POST "https://sandbox.zefort.com/api/esigns/" \
     -u $ZEFORT_APIKEY: \
     -H "Content-Type: application/json" \
     -d '{"contract": "ct_1JrjJRTzEMlHVHls", "document": "doc_2VHlsJrjJRTzEMlH", "attachments": ["doc_5zEMlHVHlsJrjJRT", "doc_7RTzEMlHVHlsJrjJ"]}'

When e-sign has multiple documents, it's possible to use needs_to_be_viewed setting. If it's true, then all attachments need to viewed before signing or approval is possible. By default it's false and only signed document needs to be viewed.

Even though attachments won't be signed, signed contract will include filenames and checksums of all attachment documents.

3. Different authentication types

There are a few things to be taken into account when using different authentication methods.

SMS authentication requires e-sign party to have phone_number.

personal_id can be given only when strong authentication is used and it will be validated based on authentication type selection. If personal ID is given, authentication needs to match with given personal ID.

E-signs have setting need_auth_to_view_documents. When this is true, all parties need to have either strong or SMS authentication.

4. Options for signing order

sign_order has three different options parallel, consecutive and flexible. Sign order limits how ordinal can be set to e-sign parties.

Parallel sign order

Parallel sign order is simpliest of all. All approvers will first get approval request. Once all approvers have approved documents, all signers will get request to sign contract. In the end all signers and CC's will receive link to final documents.

Consecutive sign order

In consecutive signing order approvers and signers will approve or sign contract in consecutive order. CC's won't have ordinal and will always just receive final documents.

Easiest way to set signing order is to send parties in one array with desired order. It's also possible to update parties' ordinals with patch request. New parties without ordinal will be set as a last one to sign or approve.

Flexible sign order

In flexible sigining order there are no limitations for setting ordinals. When all parties with given ordinal have completed their signing or approval, parties with next ordinal will receive invitation to sign or approve.

If parties are added without ordinal, those will set as a last in the process.

5. Working with e-sign after activation

Updating parties in e-sign

When e-sign has been activated, e-sign parties can still be added and existing parties edited and deleted as long as party has not signed or approved the e-sign. When e-sign is completed, no changes to parties are allowed. Documents cannot be edited after activation.

Here is an example of editing email address to signer. In this case, new signing request will be sent automatically.

import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"
PARTY_ID = "sgnr_JrjJRTzEMlHVHls1"

response = requests.patch(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/parties/{PARTY_ID}/", 
    auth=(ZEFORT_APIKEY, ""),
    json={"email": "john.doe@greatcompany.com"}
)

print(response.json())
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
ESIGN_ID="esgn_lHVHls1JrjJRTzEM"
PARTY_ID="sgnr_JrjJRTzEMlHVHls1"

curl -X PATCH "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/parties/$PARTY_ID/" \
     -u $ZEFORT_APIKEY: \
     -H "Content-Type: application/json" \
     -d '{"email": "john.doe@greatcompany.com"}'

Canceling and reactivating e-sign

It's possible to cancel signing request. All parties will be informed.

import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"

response = requests.post(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/cancel/", 
    auth=(ZEFORT_APIKEY, "")
)
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"

curl -X POST "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/cancel/" \
     -u $ZEFORT_APIKEY:

Expired or cancelled signing request can be reactivated for signing. If e-sign is expired, deadline needs to be updated before reactivation.

import datetime
import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"
deadline = datetime.datetime.now() + datetime.timedelta(weeks=2)

response = requests.patch(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/", 
    auth=(ZEFORT_APIKEY, ""),
    json={"deadline": str(deadline)}
)

response = requests.post(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/reactivate/", 
    auth=(ZEFORT_APIKEY, "")
)
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"

curl -X PATCH "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/" \
     -u $ZEFORT_APIKEY: \
     -H "Content-Type: application/json" \
     -d '{"deadline": "2023-12-31 00:00:00"}'

curl -X POST "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/reactivate/" \
     -u $ZEFORT_APIKEY:

Working with completed e-sign

When signing has been completed, availability of documents can be changed by updating available_until field. Links to final documents can be resent to all parties, who have originally received final documents.

import datetime
import requests

ZEFORT_APIKEY = "ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"
available_until = datetime.datetime.now() + datetime.timedelta(weeks=2)

response = requests.patch(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/", 
    auth=(ZEFORT_APIKEY, ""),
    json={"available_until": str(available_until)}
)

response = requests.post(
    f"https://sandbox.zefort.com/api/esigns/{ESIGN_ID}/resend_completed/", 
    auth=(ZEFORT_APIKEY, "")
)
ZEFORT_APIKEY="ySLE1mhh6lfRsYa..."
ESIGN_ID = "esgn_lHVHls1JrjJRTzEM"

curl -X PATCH "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/" \
     -u $ZEFORT_APIKEY: \
     -H "Content-Type: application/json" \
     -d '{"available_until": "2023-12-31 00:00:00"}'

curl -X POST "https://sandbox.zefort.com/api/esigns/$ESIGN_ID/resend_completed/" \
     -u $ZEFORT_APIKEY: