Introduction to the Signsoft API

Welcome to the era of innovation! In a world where technology shapes every aspect of our lives, Signosoft brings efficiency, savings and sustainability through digital signatures. With over 50 million registered subscriptions, serving customers in every corner of the globe, Signosoft is transforming the way the world does business. Goodbye wasted paper, hello to a more sustainable environment.

With over 12 million users already enjoying our platform, now is the time for you to join us too! Introducing our API, the essential tool for integrating Signosoft's powerful signature features into your software. Quickly, conveniently and securely, our API offers a unique solution, ensuring an exceptional experience for you and your users, including:

  • Document Creation: Generate documents directly within your system.
  • Send for Signature: Send documents to be signed digitally.
  • Digital Signatures: Sign documents using various authentication methods.
  • Document Management: Organize and control all your documents in one place.
  • And much more!

Business Case: Electronic Document Signing with Notifications and Post-Signing Processing

As a client, I want to upload a document, specify who needs to sign it, receive the signed document, and ensure that all parties are notified. After that, I need to forward the document elsewhere (e.g., to an archive).

Step 1: Authentication

First, authenticate with the Signosoft server using the restServerLogin API to obtain a token. This token must be included as an Authorization header in all subsequent requests.

POST /restServerLogin
{
 "login": "your_username",
 "password": "your_password"
}
       

SIMULATE YOURSELF

Input Data

Generated Code

//Your code will show that

Call response

//Your code will show that
What happened?

You will receive a unique token, which must be included in the headers of your requests to authorize your application to perform all operations on the server. Access credentials can be obtained from the Signosoft administration console.

LEARN MORE

Step 2: Uploading the Document

Next, upload the document that needs to be signed using the uploadDocument API method. Provide the following parameters:

  • docData: Base64-encoded content of the PDF document.
  • docOwner: Identification of the document owner.
  • documentName: The name of the document, displayed during the signing process.
  • type, positionX, positionY, page: Specify where signatures should be placed on the document.
  • signerlogin, signerfirstname, signersecondname: Identification of the signer.
POST /uploadDocument
                                  {
                                  "docData": "base64_encoded_document",
                                  "docOwner": "owner_id",
                                  "documentName": "Document Title",
                                  "type": "signature",
                                  "positionX": 100,
                                  "positionY": 200,
                                  "page": 1,
                                  "signerlogin": "signer_login",
                                  "signerfirstname": "Signer First",
                                  "signersecondname": "Signer Last"
                                  }
                     
What happens during the upload process?

Simply put, documents are uploaded in an encoded format containing information about the signers and the types of signatures. The server receives the documents, stores them, and forwards them to the respective signers.

LEARN MORE

Step 4: Signing the Document

The signer connects to the provided Signosoft URL to fill in the required signatures. After signing, they click the "Finalize" button unless automatic completion is enabled.

With the generated URL, developers can:

  • Redirect users directly to the Signosoft URL.
  • Open the document in a new browser window.
  • Embed the signing interface using an iframe in your web application.

Step 5: Notification and Finalization (Webhook)

After all signatures are completed, Signosoft initiates finalization and sends an HTTP request to the specified finalizeWebhookUrl with:

  • docId and docToken: Identification of the finalized document.
  • operation: Information about the completion, e.g., "document.finalized".
  • finalizeImplResult and finalizeImplDesc: Results of the finalization plugin.
GET /finalizeWebhookUrl
                     {
                     "docId": "finalized_document_id",
                     "docToken": "finalized_document_token",
                     "operation": "document.finalized"
                     }
                     
Automate operations

With the webhook functionality, Signosoft notifies your application whenever an event occurs related to a document, making processes faster and more automated.

LEARN MORE

Step 6: Post-Signing Document Processing

Upon receiving the notification, initiate the process to send the document to another system. Options include:

  • Automatic document export using downloadDocument.
  • Integration with DMS or third-party API for direct uploads to an archive.
Automate downloads

Using this feature, you can download documents efficiently, with the option to secure them with a password for added protection.

LEARN MORE

Step 7: Deleting the PDF Document from Signosoft Server

After successful archiving, delete the document from the Signosoft server using:

DELETE /deleteDocument
                     {
                     "docToken": "document_token"
                     }
                     

This step is crucial for GDPR compliance and other data protection regulations.

Automate deletions

This functionality allows you to delete documents, templates, and contracts in a single request, enabling the removal of multiple items at once.

LEARN MORE

Example: Using the Finalization Plugin

If using the finalization plugin, define logic to send the document to another system within the plugin itself. When calling createDocLink, include finalizeEnablePlugin and ensure REST calls to upload the document post-finalization.

Summary

This process leverages essential Signosoft API methods:

This approach ensures a smooth signing process, timely notifications for all parties, and readiness for document archiving or further processing, providing a comprehensive end-to-end solution for electronic signing.


The diagram below illustrates the basic operation of the API. Each circle represents an endpoint, which can be divided into four categories: Documents, Contracts, Document Links, and Signatures. The entire operation revolves around the production of tokens. When creating a document or contract, a token is generated, which allows it to be related to other endpoints. Note that before using any endpoint, it is necessary to complete the authentication process.

As you read through the documentation, each endpoint will be detailed more specifically.

Initial instructions


To provide a clearer understanding of the documentation, the explanation of each endpoint is divided into five sections:

  1. Description - In this subsection, the functionality of the endpoint is explained, sometimes with real use cases.
  2. Parameter - In this subsection, the required and optional parameters necessary for the correct operation of the feature are detailed.
  3. How to implement - In this subsection, implementation examples are provided in the main technologies on the market, such as Java, PHP, Python, and Go.
  4. Returns - In this subsection, the returns provided by the API are detailed in JSON format, along with an explanation of each one.
  5. Try Out - This button redirects to Swagger, allowing you to test the functionality in real-time.


Authentication

The API has authentication with Bearer Token, which is one of the most common and secure methods to protect APIs. In this model, an access token is generated after the user or application login process, and must be included in the header of each subsequent request. This token acts as an access key, validating the identity of the requester and ensuring that only authorized users can interact with the API. The use of Bearer Tokens is crucial to maintain the integrity and security of information, preventing unauthorized access and protecting sensitive data transmitted between the client and the server.

Description

When a client authenticates via the /restServerLogin route, it receives a key (called a token) that allows the use of other resources. This token is required to create and sign documents. To use routes other than user creation, you must provide this token in the request header. We'll talk more about this later.

Creation of credentials for authentication is carried out in the administration console.

The diagram below demonstrates the authentication process, when the user has their authentication credentials.

1) To authenticate, the user enters their credentials.
On the Signosoft server (in the database in the Servers table or in the administration console), the customer must create a trusted server.
2) The server receives the JSON and verifies the data.
3) If the data is correct, the server responds with an authorization token, which will be needed for future requests.

Administration console: Administrative panel that customers access to manage units, configure settings and create API access users.
Token: An OAuth token is a key that allows an application to access a user's data on another service without needing the user's password.

Parameters

To carry out the request, some parameters are mandatory. See the list below:

  • loginString – The access login created in the administration console. Required
  • passwordString – The access password. Required

How to implement

You can implement the function of creating users in different places on your system. See an example of implementation below:


import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;

public class PostRequestExample {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        String uri = "https://context/api/restServerLogin";

        String json = "{\"login\":\"your_login\", \"password\":\"your_passowrd\"}";
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(uri))
            .header("Content-Type", "application/json")
            .POST(BodyPublishers.ofString(json))
            .build();

        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .thenAccept(System.out::println)
            .join();
    }
}                                  
                  

import requests

url = "https://context/api/restServerLogin"
payload = {
    "login": "your_login",
    "password": "your_passowrd"
}
headers = {
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.status_code)
print(response.json())      
              

  <?php
$url = 'https://context/api/restServerLogin';
$data = array('login' => 'your_login', 'password' => 'your_passowrd');
$options = array(
    'http' => array(
        'header'  => "Content-Type: application/json\r\n",
        'method'  => 'POST',
        'content' => json_encode($data),
    ),
);

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
    /* Handle error */
}

var_dump($result);
  ?>   
                

package main

import (
    "bytes"
    "fmt"
    "net/http"
)

func main() {
    url := "https://context/api/restServerLogin"
    json := []byte(`{"login":"your_login", "password":"your_password"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(json))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("response Status:", resp.Status)
    fmt.Println("response Headers:", resp.Header)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println("response Body:", string(body))
}   
                

Returns

When the user is validated correctly, it returns a status code 200, providing some important information, for example:


{
  "tokenExp": 1717161555540,
  "result": "OK",
  "tokenExpIn": 3599,
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJsaW4iOiJ.."
}
              
  • tokenExpLong – Expiration time in UNIX format.
  • resultString – Result of the operation
  • tokenExpInToken – How many seconds remain until the token expires.
  • tokenString – Authorization token.

The Unix timestamp is the number of seconds since January 1, 1970, used to represent dates and times in computer systems.
The advantage of the Unix timestamp is its simplicity and consistency, allowing easy storage and comparison of dates and times without complications with time zones.

Authenticating application

Documents

Uploading documents, such as PDF and DOC files, is an essential feature in many modern platforms. This functionality allows users to send files directly to the server, facilitating the storage, sharing, and processing of important information. By supporting formats like PDF and DOC, the platform ensures compatibility with the most commonly used file types, providing a seamless and efficient user experience. Additionally, the upload process must be secure and reliable, ensuring that documents are transmitted and stored in a manner that protects them from unauthorized access.

Uploading documents

Description

Uploading documents with our API is quite simple. All documents are sent encoded in Base64, and you can set various configurations for the document. Later on, you will see some examples with use cases to understand how easy it is to integrate with your system.

Base64 is an encoding scheme that allows you to convert binary data (such as images, documents, or other types of files) into a string of text characters. This is done to ensure that data can be transmitted securely through text-only systems such as emails and URLs.

Imagine you want to send an image via email. Emails were originally designed to transmit text. If you send the image file as binary, it may not be interpreted correctly by the email server, resulting in data loss or crashes. By encoding the image in Base64, you transform the image data into text, which can be easily included in the body of the email or as a secure attachment.
  • Compatibility with text systems:

    Many data transmission systems (such as emails and URLs) were originally designed to handle text only. Base64 ensures that binary data can be transmitted across these systems without loss or corruption.

  • Security and integrity:

    Because Base64 encoding transforms binary data into text, there is less risk of problems related to special or control characters that could interfere with data transmission.

  • Ease of handling:

    Base64-encoded data can be manipulated as text in scripts, configuration files, and other contexts where working directly with binaries may be complicated or unsupported.

The upload process follows three main steps, which we will explain below:

The first step to be performed in the client application is authentication using the /restServerLogin method. This step is essential because this key (token) allows the Signosoft server to permit the document upload. This token will also be necessary for any other method in the future.

The second step is the document upload that will be used for signing. Any upload method can be used by the client (a file input field, for example).
After the upload, the client application must encode this file in Base64. Most technologies have built-in resources for this, for example, in JavaScript, you can use the FileReader function.

With the authorization token in hand and the file encoded, simply call the /REST/uploadDocument route and provide the required and optional data (such as name, owner, etc.).
The request is sent to the Signosoft server, which will verify if all parameters are met (in this process, the token is sent) and if the encoded document is valid. If there is any issue, the server will return a message to the client application, informing what happened. If everything is correct, the server will decode the document and store it in a specific location for documents. At the same time, the information is saved in the database, and the server sends a success message to the client application.

Parameters

To perform the request, some parameters are mandatory while others are optional. See the list below:

  • docDataString – This is the document you want to upload, encoded in BASE64 format. It must be a PDF. Required
  • docOwnerString – This is the email of the user who owns or created the document. Required
  • documentNameString – This is the name of the document that will be shown during the signing process. Required
  • applicationModeString – This specifies if users can modify the signature fields in the document. It can be either ‘SIGN’ or ‘EDIT’. If not provided, it defaults to “EDIT“. In EDIT mode, users can change document fields (like the size and position of the signature field) before sending it for signing. In SIGN mode, users can only fill out existing fields and sign the document without making any changes. Optional
  • commandsJSON – Additional information related to the document, formatted as a JSON object. Optional
  • docExtensionString – If your document is not a PDF, you can use this parameter to specify its format. The Signosoft server can convert it if configured properly. Optional
  • deadlineLong – This is the expiration date and time for the document, in Unix timestamp format (epoch). Optional
  • attachmentsJSON – A JSON object that contains data and metadata about any documents attached to the document you're uploading for signature. Optional
  • parametersString – A string of parameters separated by semicolons (‘;’). Each parameter value is separated by a vertical bar (‘|’). These parameters define various fields and properties for the document. Here are some key ones:
    • type – Type of the field to be added, possible values include SIGNATURE, TEXT, CHECKBOX, RADIOBUTTON, ALLSIGNATURE.
    • subtype – Optional for type=SIGNATURE. If not specified, it defaults to ‘biometric’. Other options are ‘click-to-sign’, ‘one-time-password’, ‘bank-id’, ‘bank-id-sign’, and ‘sms-password’.
    • name – Name of the field. It is used to group selection buttons such as checkboxes and radio buttons. For example, each radio button that shares the same name is treated as part of a single group, allowing the user to select only one option within that group.
    • lookup – Text to search for in the document to use as an anchor for positioning fields.
    • offsetX – X offset from the anchor's bottom left corner, as a percentage of the page width.
    • offsetY – Y offset from the anchor's bottom left corner, as a percentage of the page height.
    • page – Page number where the field will be placed, starting from 1.
    • positionX – X position of the field on the page, as a percentage of the page width.
    • positionY – Y position of the field on the page, as a percentage of the page height.
    • width – Width of the field, as a percentage of the page width.
    • height – Height of the field, as a percentage of the page height.
    • order – The order in which fields should be filled or signed.
    • required – Indicates if the field is mandatory.
    • signerlogin – Signer’s login information.
    • signerfirstname – Signer’s first name.
    • signersecondname – Signer’s second name.
    • signerpassword – Extra password for the signer, used with the one-time-password subtype.
    • signerphone – Phone number of the signer for sms-password subtype.
    • value – Prefilled value for a text field.
    • inperson – Indicates if the signature is in-person. If true, the signer doesn't need to be defined and is not notified by email.
    • disableNotifications – If true, disables all email notifications about the document.
    • commands – Additional information related to the signature.
    • bgcolor – Background color of the signature image in hexadecimal format (e.g., “#f2f2f2”).
    • image – Base64 encoded image for the signature.
    • selected – Used to indicate whether a field is selected. Its possible values are true or false.
    Optional

How to implement

In the example below, a simple upload is done, with the mandatory parameters.


import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.charset.StandardCharsets;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.net.URLEncoder;
import java.net.http.HttpRequest.BodyPublishers;
import org.json.JSONObject;

public class UploadDocument {

    public static void main(String[] args) throws IOException, InterruptedException {
        String login = "username";
        String password = "password";
        File pdfFile = new File("path/to/your/file.pdf");
        String documentName = "document_name.pdf";
        String docOwner = "email@example.com";

        // Authenticate and get token
        String token = authenticate(login, password);

        // Upload the document
        uploadDocument(pdfFile, documentName, docOwner, token);
    }

    // Function to authenticate and get token
    private static String authenticate(String login, String password) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        Map data = new HashMap<>();
        data.put("login", login);
        data.put("password", password);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://context/api/restServerLogin"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(ofFormData(data))
                .build();

        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        
        // Extract token from JSON response
        JSONObject jsonObject = new JSONObject(response.body());
        return jsonObject.getString("token");
    }

    // Function to upload document
    private static void uploadDocument(File pdfFile, String documentName, String docOwner, String token) throws IOException, InterruptedException {
        // Convert PDF to Base64
        byte[] fileContent = Files.readAllBytes(pdfFile.toPath());
        String encodedString = Base64.getEncoder().encodeToString(fileContent);

        // Prepare data for POST request
        Map data = new HashMap<>();
        data.put("docData", encodedString);
        data.put("documentName", documentName);
        data.put("docOwner", docOwner);

        // Send POST request to upload document
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://context/api/REST/uploadDocument"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer " + token)
                .POST(ofFormData(data))
                .build();

        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
        
        // Print response
        System.out.println(response.body());
    }

    // Helper function to convert form data to x-www-form-urlencoded format
    private static HttpRequest.BodyPublisher ofFormData(Map data) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry entry : data.entrySet()) {
            if (builder.length() > 0) {
                builder.append("&");
            }
            builder.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
            builder.append("=");
            builder.append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8));
        }
        return BodyPublishers.ofString(builder.toString());
    }
}
                      
                  
                  

import requests
import base64

def authenticate(login, password):
    """
    Authenticate the user and return the authentication token.
    """
    url = "http://context/api/restServerLogin"
    payload = {'login': login, 'password': password}
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    
    response = requests.post(url, data=payload, headers=headers)
    response.raise_for_status()  # Raise an error for bad status codes
    token = response.json().get("token")
    
    return token

def upload_document(file_path, document_name, doc_owner, token):
    """
    Upload a PDF document encoded in Base64.
    """
    # Read and encode the PDF file in Base64
    with open(file_path, "rb") as pdf_file:
        encoded_string = base64.b64encode(pdf_file.read()).decode('utf-8')
    
    payload = {
        'docData': encoded_string,
        'documentName': document_name,
        'docOwner': doc_owner
    }
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': f'Bearer {token}'
    }
    
    # Send POST request to upload the document
    response = requests.post("http://context/api/REST/uploadDocument", data=payload, headers=headers)
    response.raise_for_status()
    print(response.text)

if __name__ == "__main__":
    login = "username"
    password = "password"
    file_path = "path/to/your/file.pdf"
    document_name = "document_name.pdf"
    doc_owner = "email@example.com"
    
    # Authenticate and get the token
    token = authenticate(login, password)
    
    # Upload the document
    upload_document(file_path, document_name, doc_owner, token)
              

  <?php
  function authenticate($login, $password) {
    // Authenticate the user and return the authentication token
    $url = "http://context/api/restServerLogin";
    $data = http_build_query(array('login' => $login, 'password' => $password));
    
    $options = array(
        'http' => array(
            'header'  => "Content-Type: application/x-www-form-urlencoded\r\n",
            'method'  => 'POST',
            'content' => $data,
        ),
    );
    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);
    
    if ($response === FALSE) { 
        die('Error'); 
    }
    
    $responseData = json_decode($response, true);
    return $responseData['token'];
}

function uploadDocument($filePath, $documentName, $docOwner, $token) {
    // Upload a PDF document encoded in Base64
    $url = "http://context/api/REST/uploadDocument";
    
    // Read and encode the PDF file in Base64
    $pdfContent = file_get_contents($filePath);
    $encodedString = base64_encode($pdfContent);
    
    $data = http_build_query(array(
        'docData' => $encodedString,
        'documentName' => $documentName,
        'docOwner' => $docOwner
    ));
    
    $options = array(
        'http' => array(
            'header'  => "Content-Type: application/x-www-form-urlencoded\r\n" .
                         "Authorization: Bearer $token\r\n",
            'method'  => 'POST',
            'content' => $data,
        ),
    );
    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);
    
    if ($response === FALSE) { 
        die('Error'); 
    }
    
    echo $response;
}

$login = "username";
$password = "password";
$filePath = "path/to/your/file.pdf";
$documentName = "document_name.pdf";
$docOwner = "email@example.com";

// Authenticate and get the token
$token = authenticate($login, $password);

// Upload the document
uploadDocument($filePath, $documentName, $docOwner, $token);
  ?>   
                

package main

import (
    "bytes"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
)

func authenticate(login, password string) (string, error) {
    // Authenticate the user and return the authentication token
    data := url.Values{}
    data.Set("login", login)
    data.Set("password", password)

    resp, err := http.PostForm("http://context/api/restServerLogin", data)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return "", fmt.Errorf("failed to authenticate: %s", resp.Status)
    }

    var result map[string]string
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return "", err
    }

    return result["token"], nil
}

func uploadDocument(filePath, documentName, docOwner, token string) error {
    // Upload a PDF document encoded in Base64
    fileContent, err := ioutil.ReadFile(filePath)
    if err != nil {
        return err
    }

    // Encode the PDF file in Base64
    encodedString := base64.StdEncoding.EncodeToString(fileContent)

    data := url.Values{}
    data.Set("docData", encodedString)
    data.Set("documentName", documentName)
    data.Set("docOwner", docOwner)

    req, err := http.NewRequest("POST", "http://context/api/REST/uploadDocument", bytes.NewBufferString(data.Encode()))
    if err != nil {
        return err
    }

    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Set("Authorization", "Bearer "+token)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("failed to upload document: %s", resp.Status)
    }

    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))

    return nil
}

func main() {
    login := "username"
    password := "password"
    filePath := "path/to/your/file.pdf"
    documentName := "document_name.pdf"
    docOwner := "email@example.com"

    // Authenticate and get the token
    token, err := authenticate(login, password)
    if err != nil {
        fmt.Println("Error authenticating:", err)
        return
    }

    // Upload the document
    if err := uploadDocument(filePath, documentName, docOwner, token); err != nil {
        fmt.Println("Error uploading document:", err)
        return
    }
}
                

Returns

When the document loads correctly, it returns a status code of 200, providing some important information, for example:


{
    "uploadResult": "OK",
    "document": {
        "docid": 13175,
        "docowner": "test@test.com",
        "docdeadline": 0,
        "docstate": "New",
        "docsize": 270804,
        "docMode": "EDIT",
        "docname": "document_name",
        "doctoken": "4dc0faa7-0434-4e3e-8e2a-4451ddac688d_1717644167527"
    },
    "signatures": []
}
                
  • uploadResultString – Result of the upload operation.
  • docidInteger – ID of the document.
  • docownerString – Email of the document owner.
  • docdeadlineLong – Deadline of the document in UNIX format.
  • docstateString – State of the document.
  • docsizeInteger – Size of the document in bytes.
  • docModeString – Mode of the document (e.g., EDIT).
  • docnameString – Name of the document.
  • doctokenString – Token associated with the document.
  • signaturesArray – List of signatures associated with the document.

What if it was necessary to create a document with more than one signer, using different methods?

To add multiple subscribers to an upload, with different methods, simply use the parameters option. This parameter receives a string, where each created field (whether a signature, checkbox or text field) must be separated by ";", and each option within a created field must be separated by "|". It may seem confusing, but you will find that implementing this feature is simple. Let's look at a practical example.

Imagine that you want to send a document to three people to sign in different ways. Imagine that this document has two pages, and the signatures should be on the last page next to each other.
Filip - Will sign in a common way, using the biometric option.
Name: Filip Smith
Email: fpsmith@company.com


Carl - You will sign using a unique password.
Name: Carl Johnson
Email: carljsn@company.com


Paul - Will sign with a cell phone token.
Name: Paul David
Email: pauldavid@company.com
Telephone: (420) 776 647 547


You can create four types of fields as shown in the image below. The "signature" type has six subtypes that must be specified when creating a signature field. If you do not specify, the "biometric" option will be selected by default.

For each signature subtype, there are other options that you can find in the "MORE OPTIONS FOR SIGNATURE" box. In the "OTHERS" box, there are options that can be applied to other types of fields, such as position, order, height, among others.

See what the parameters for the Filip:

                  parameters: "type=signature|signerlogin=fpsmith@company.com|signerfirstname=Filip|signersecondname=Smith|page=2|positionY=80|positionX=20|required=true"
                

See what the parameters for the Carl:

                  parameters: "type=signature|subtype=one-time-password|signerlogin=carljsn@company.com|signerfirstname=Carl|signersecondname=Johnson|page=2|positionY=80|positionX=40|required=true|signerpassword=123456"
                

See what the parameters for the Carl:

                  parameters: "type=signature|subtype=sms-password|signerlogin=pauldavid@company.com|signerfirstname=Paul|signersecondname=David|page=2|positionY=80|positionX=60|required=true|signerphone=420776647547"
                

To upload a document and add multiple signers with different signature methods, you must join all the parameters into a single string, separating each parameter with ";". This will be the end result

                  parameters: "type=signature|signerlogin=fpsmith@company.com|signerfirstname=Filip|signersecondname=Smith|page=2|positionY=80|positionX=20|required=true; 
type=signature|subtype=one-time-password|signerlogin=carljsn@company.com|signerfirstname=Carl|signersecondname=Johnson|page=2|positionY=80|positionX=40|required=true|signerpassword=123456; 
type=signature|subtype=sms-password|signerlogin=pauldavid@company.com|signerfirstname=Paul|signersecondname=David|page=2|positionY=80|positionX=60|required=true|signerphone=420776647547"
                
This was an example with signature fields, but the same concept can be applied to text and checkbox fields. You can create as many fields as you want and position them wherever you want, based on position attributes, whose unit of measurement is the percentage (%) of the document page. There is no specific order for the attributes; just enter the attributes separated by "|". This is what our final implementation would look like.
Uploading document

Download documents

Description

After uploading documents, you must later download these documents. It's very simple to download documents: just specify the document token and, within a few seconds, the document will be available.

The download process is basically the same as the upload, but simpler. See how it works:

1) The application specifies the document token, with the other parameters (in the parameters section).
2) The server checks the authorization token and whether the document exists.
3) If everything is ok, the server will return the document in base64. The client application can convert base64 to the original format for necessary actions.

Parameters

To perform the request, some parameters are mandatory. See the list below:

  • docTokenString – The token is generated when the document is sent to the server, and is used to specifically reference that document for future operations, such as downloading.
  • flattenedBoolean – When set to 'true', the document will be flattened, meaning it will be stripped of all editable objects, including biometric data. This is typically done to prevent future editing or to protect sensitive information.
  • lockBoolean – When set to 'true', the document will be password protected. However, if the document is already signed, it cannot be locked as the locking mechanism would invalidate the existing signature. In other words, you can only password protect documents that have not been electronically signed.
  • toFileBoolean – When toFile is set to false, it indicates that the document download request was made by an external system, possibly through an API or integration with another service.

How to implement

In the example below, a simple upload is done, with the mandatory parameters.


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DownloadDocument {

    public static void main(String[] args) throws IOException, InterruptedException {
        String docToken = "TOKEN_DOCUMENT";
        String flattened = "true";
        String lock = "false";
        String toFile = "false";

        // Authenticate and get token (you need to implement this part)
        String token = "••••••"; // Replace with your actual authentication token

        // Execute the document download
        downloadDocument(docToken, flattened, lock, toFile, token);
    }

    // Function to download document
    private static void downloadDocument(String docToken, String flattened, String lock, String toFile, String token) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();

        // Prepare data for POST request
        Map<String, String> data = new HashMap<>();
        data.put("docToken", docToken);
        data.put("flattened", flattened);
        data.put("lock", lock);
        data.put("toFile", toFile);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://test.signosoft.com/api/REST/downloadDoc"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer " + token) // Include Authorization header with Bearer token
                .POST(ofFormData(data))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Print response
        System.out.println(response.body());
    }

    // Helper function to convert form data to x-www-form-urlencoded format
    private static HttpRequest.BodyPublisher ofFormData(Map<String, String> data) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            if (builder.length() > 0) {
                builder.append("&");
            }
            builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
            builder.append("=");
            builder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return HttpRequest.BodyPublishers.ofString(builder.toString());
    }
}                     
                  

import requests

def download_document():
    url = 'https://test.signosoft.com/api/REST/downloadDoc'
    doc_token = 'TOKEN_DOCUMENT'
    flattened = 'true'
    lock = 'false'
    to_file = 'false'
    token = '••••••'  # Replace with your actual authentication token

    params = {
        'docToken': doc_token,
        'flattened': flattened,
        'lock': lock,
        'toFile': to_file
    }

    headers = {
        'Authorization': 'Bearer ' + token,
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    response = requests.post(url, headers=headers, data=params)
    print(response.text)

download_document()
              

<?php
function downloadDocument() {
  $url = 'https://signosoft.com/api/REST/downloadDoc';
  $docToken = 'TOKEN_DOCUMENT';
  $flattened = 'true';
  $lock = 'false';
  $toFile = 'false';
  $token = '••••••'; // Replace with your actual authentication token

  $data = array(
      'docToken' => $docToken,
      'flattened' => $flattened,
      'lock' => $lock,
      'toFile' => $toFile
  );

  $headers = array(
      'Authorization: Bearer ' . $token,
      'Content-Type: application/x-www-form-urlencoded'
  );

  $options = array(
      'http' => array(
          'header' => implode("\r\n", $headers),
          'method' => 'POST',
          'content' => http_build_query($data)
      )
  );

  $context = stream_context_create($options);
  $result = file_get_contents($url, false, $context);

  if ($result === FALSE) {
      echo "Error";
  } else {
      echo $result;
  }
}

downloadDocument();n);
  ?>   
                

package main

import (
    "fmt"
    "net/http"
    "net/url"
)

func main() {
    downloadDocument()
}

func downloadDocument() {
    url := "https://test.signosoft.com/api/REST/downloadDoc"
    docToken := "TOKEN_DOCUMENT"
    flattened := "true"
    lock := "false"
    toFile := "false"
    token := "••••••" // Replace with your actual authentication token

    data := url.Values{}
    data.Set("docToken", docToken)
    data.Set("flattened", flattened)
    data.Set("lock", lock)
    data.Set("toFile", toFile)

    req, err := http.NewRequest("POST", url, nil)
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }

    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

    req.URL.RawQuery = data.Encode()

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return
    }
    defer resp.Body.Close()

    fmt.Println("Response Status:", resp.Status)

    // Print response body
    buf := make([]byte, 1024)
    for {
        n, err := resp.Body.Read(buf)
        if n == 0 || err != nil {
            break
        }
        fmt.Print(string(buf[:n]))
    }
}
                

Returns

This will be the return for both requests above:

{
  "flattened": true,
  "docName": "NAME_DOCUMENT",
  "downloadDocResult": "OK",
  "locked": false,
  "docData": "JVBERi0xLjcKJfbk/N8KMSAwIG9iago8PAovUGFnZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1ZpZXdlclByZWZlcmVuY2VzIDw8Ci9EaXJlY3Rpb24gL0wyUgo+PgovQWNyb0Zvcm0gMyAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0NyZWF0aW9uRGF0ZSAoRDoyMDI0MDcwMjA5Mzc1MikKL0NyZWF0b3..."
}

See that the request returns are the same parameters informed in the request.

Download document

Delete documents

Description

It is very simple to delete documents from the server. Just specify the tokens of the documents you want to delete, and they will be deleted.

Tokens must be passed inside "[]", separated by a comma. Even if you are only going to delete one document, you must follow this pattern.

Parameters

To perform the request, the following parameter is mandatory:

  • docTokensString – A JSON array string containing the tokens of the documents or contracts you want to delete. Each token uniquely identifies a document or contract on the server. For example:
    [“4819d2d0-c8d5-41d6-bc66-7832bc5806d4”, “4819d2d0-c8d5-41d6-bc66-7832bc5806d5”]

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DeleteDocuments {

    public static void main(String[] args) throws IOException, InterruptedException {
        String docTokens = "[\"71ae7c01-1c66-483a-b5a9-f68a20449555_1719417913870\", \"bab1fb1b-f0f6-4a8b-b1c2-ef0197ce2096_1719941381403\"]";
        String token = "••••••"; // Replace with your actual authentication token

        // Execute the document deletion
        deleteDocuments(docTokens, token);
    }

    // Function to delete documents
    private static void deleteDocuments(String docTokens, String token) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();

        // Prepare data for POST request
        Map<String, String> data = new HashMap<>();
        data.put("docTokens", docTokens);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://test.signosoft.com/api/REST/deleteDocument"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer " + token) // Include Authorization header with Bearer token
                .POST(ofFormData(data))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Print response
        System.out.println(response.body());
    }

    // Helper function to convert form data to x-www-form-urlencoded format
    private static HttpRequest.BodyPublisher ofFormData(Map<String, String> data) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            if (builder.length() > 0) {
                builder.append("&");
            }
            builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
            builder.append("=");
            builder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return HttpRequest.BodyPublishers.ofString(builder.toString());
    }
}                             
                  

import requests

def delete_documents():
    url = 'https://signosoft.com/api/REST/deleteDocument'
    doc_tokens = '["71ae7c01-1c66-483a-b5a9-f68a20449555_1719417913870", "bab1fb1b-f0f6-4a8b-b1c2-ef0197ce2096_1719941381403"]'
    token = '••••••'  # Replace with your actual authentication token

    params = {
        'docTokens': doc_tokens
    }

    headers = {
        'Authorization': 'Bearer ' + token,
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    response = requests.post(url, headers=headers, data=params)
    print(response.text)

delete_documents()   
              

  <?php
  function deleteDocuments() {
    $url = 'https://test.signosoft.com/api/REST/deleteDocument';
    $docTokens = '["71ae7c01-1c66-483a-b5a9-f68a20449555_1719417913870", "bab1fb1b-f0f6-4a8b-b1c2-ef0197ce2096_1719941381403"]';
    $token = '••••••'; // Replace with your actual authentication token

    $data = array(
        'docTokens' => $docTokens
    );

    $headers = array(
        'Authorization: Bearer ' . $token,
        'Content-Type: application/x-www-form-urlencoded'
    );

    $options = array(
        'http' => array(
            'header' => implode("\r\n", $headers),
            'method' => 'POST',
            'content' => http_build_query($data)
        )
    );

    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    if ($result === FALSE) {
        echo "Error";
    } else {
        echo $result;
    }
  }

  deleteDocuments();
  ?>   
                

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strings"
)

func main() {
    deleteDocuments()
}

func deleteDocuments() {
    urlStr := "https://test.signosoft.com/api/REST/deleteDocument"
    docTokens := `["71ae7c01-1c66-483a-b5a9-f68a20449555_1719417913870", "bab1fb1b-f0f6-4a8b-b1c2-ef0197ce2096_1719941381403"]`
    token := "••••••" // Replace with your actual authentication token

    data := url.Values{}
    data.Set("docTokens", docTokens)

    req, err := http.NewRequest("POST", urlStr, strings.NewReader(data.Encode()))
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }

    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return
    }
    defer resp.Body.Close()

    fmt.Println("Response Status:", resp.Status)

    // Print response body
    buf := make([]byte, 1024)
    for {
        n, err := resp.Body.Read(buf)
        if n == 0 || err != nil {
            break
        }
        fmt.Print(string(buf[:n]))
    }
}                   
                

Returns

{
  "deleteResult": "DELETED",
  "deleteResults": [
    {
      "token": "71ae7c01-1c66-483a-b5a9-f68a20449555_1719417913870",
      "status": "DELETED"
    },
    {
      "token": "bab1fb1b-f0f6-4a8b-b1c2-ef0197ce2096_1719941381403",
      "status": "DELETED"
    }
  ]
}
                
Delete document

Get documents by token

Description

It is often necessary to search for complete information about a given document, such as who the owner is, who the signers are, and the status of the document (if it has already been signed or is waiting). All this information for a given document can be obtained using the getDocumentByToken route.

This route does not download the document itself; she is responsible for providing complete document information.

Imagine that you want to implement a functionality in your system and, for some reason, you need to know if the document was signed by all signatories. Using this route, you receive a docstate attribute, which indicates the state of the document. If signed, the attribute will contain SIGNED.

Parameters

To perform the request, the following parameter is mandatory:

  • doctokenString – This is the unique identification token of the document. You receive this token when you upload the document to the server.

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class GetDocumentByToken {

    public static void main(String[] args) throws IOException, InterruptedException {
        String docToken = "151ec53b-2aa2-40ac-abb5-fec96e425e03_1720032817699";
        String token_auth = "••••••";

        getDocumentByToken(docToken, token_auth);
    }

    // Function to get document by token
    private static void getDocumentByToken(String docToken, String token) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();

        String url = "https://signosoft.com/api/REST/getDocumentByToken?doctoken=" + docToken;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("accept", "application/json;charset=UTF-8")
                .header("Authorization", "Bearer " + token_auth)
                .POST(HttpRequest.BodyPublishers.noBody())
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}       
                  

import requests

def get_document_by_token():
    url = 'https://signosoft.com/api/REST/getDocumentByToken'
    doc_token = '151ec53b-2aa2-40ac-abb5-fec96e425e03_1720032817699'
    token_auth = '••••••'

    headers = {
        'accept': 'application/json;charset=UTF-8',
        'Authorization': 'Bearer ' + token_auth
    }

    response = requests.post(url + '?doctoken=' + doc_token, headers=headers)
    print(response.text)

get_document_by_token()
              

  <?php
function getDocumentByToken() {
  $url = 'https://signosoft.com/api/REST/getDocumentByToken?doctoken=151ec53b-2aa2-40ac-abb5-fec96e425e03_1720032817699';
  $token_auth = '••••••';

  $headers = array(
      'accept: application/json;charset=UTF-8',
      'Authorization: Bearer ' . $token_auth
  );

  $options = array(
      'http' => array(
          'header' => implode("\r\n", $headers),
          'method' => 'POST',
          'content' => ''  // No body content
      )
  );

  $context = stream_context_create($options);
  $result = file_get_contents($url, false, $context);

  if ($result === FALSE) {
      echo "Error";
  } else {
      echo $result;
  }
}

getDocumentByToken();
  ?>   
                

package main

import (
    "fmt"
    "net/http"
    "strings"
)

func main() {
    getDocumentByToken()
}

func getDocumentByToken() {
    url := "https://test.signosoft.com/api/REST/getDocumentByToken?doctoken=151ec53b-2aa2-40ac-abb5-fec96e425e03_1720032817699"
    token_auth := "••••••"

    req, err := http.NewRequest("POST", url, strings.NewReader(""))
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }

    req.Header.Set("accept", "application/json;charset=UTF-8")
    req.Header.Set("Authorization", "Bearer "+token_auth)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return
    }
    defer resp.Body.Close()

    fmt.Println("Response Status:", resp.Status)

    // Print response body
    buf := make([]byte, 1024)
    for {
        n, err := resp.Body.Read(buf)
        if n == 0 || err != nil {
            break
        }
        fmt.Print(string(buf[:n]))
    }
}                   
                

Returns

{
  "user":{
    "userid":7,
    "useremail":"exemple@example.com",
    "userFirstName":"John",
    "userSecondName":"Paul",
    "usertoken":"eyJhbGciOiJIUzUxMiJ9.eyJsaW4iOiJzaWdub3NvZnQiLCJzdWIiOiJ1c2VyIGF1dGhlbnRpemF0aW9uIiwiaXNzIjoiU2lnbm9Tb2Z0U2VydmVyIiwiZXhwIjoxNTU2MTA3Mzg0fQ.hA8G9O2TvDkm1GqJsTtjeXA_PQpsolGGjAg3HpUDlzDQ5UQVRpmI72vCLPhpevz9qO9MhlTzQHuv9jd42jgh0A",
    "userrole":1
  },
  "docObject":{
    "docid":4472,
    "docname":"contract1",
    "docstate":"Ready to sign",
    "docstateid":2
    "docowner":"exemple@example.com",
    "docsize":0,
    "docMode":"SIGN",
    "doctoken":"c18448de-a17b-48ae-9a7d-826ba5a51b94_1532331797448",
    "docdate":1532331822000
  },
  "signatures":[
    {
      "sigid":8671,
      "signame":"sig1",
      "sigsigner":"carljohson@company.com",
      "sigsignerfirstname":"Carl",
      "sigsignersecondname":"Johnson",
      "sigsigned":false,
      "sigwidth":23.0,
      "sigheight":7.0,
      "sigx":12.5,
      "sigy":91.33999633789062,
      "sigpage":1,
      "sigorder":2,
      "sigmethod":"3",
      "sigimage":""
    },
    {
      "sigid":8672,
      "signame":"sig2",
      "sigsigner":"juliosf@company.com",
      "sigsignerfirstname":"Julios",
      "sigsignersecondname":"Thuran",
      "sigsigned":true,
      "sigwidth":23.0,
      "sigheight":7.0,
      "sigx":65.55999755859375,
      "sigy":91.33999633789062,
      "sigpage":1,
      "sigorder":1,
      "sigmethod":"4",
      "sigimage":""
    }
  ],
  "getDocResult":"OK"
}
                

The callbacks are:

  • user: Document owner details.
  • docObject: Details of the document itself, such as the document status and other relevant information.
  • signatures: Details of signatories added to the document. Each signer is a sub-object, containing complete information for each signature.

Imagine that you need to implement a control function to check which signers have not signed a certain document. Just check if the 'sigsigned' attribute is set to false, indicating that that specific signature has not yet been signed.

Get Document By Token

Document Report

Description

In the previous sections, we saw how to upload a document, download the document, delete it and obtain complete information about it. Now, imagine that you need a detailed report of what happened with a certain document, from its creation to the last signature. This can be achieved via the /downloadDocReport route.

Operation

How it works is very simple: just enter the document token, and the server will return a PDF in base64, containing the entire history of the document, similar to the image below.

Parameters

To perform the request, the following parameter is mandatory:

  • docTokenString – This is the unique identification token of the document for which you want to download the report. You receive this token when you upload the document to the server.Required

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class DownloadDocReport {

    public static void main(String[] args) throws IOException, InterruptedException {
        String docToken = "e1e5efc1-6874-44ee-af24-e74493916f2b_1719417589096";
        String token_auth = "••••••";

        downloadDocReport(docToken, token);
    }

    // Function to download document report by token
    private static void downloadDocReport(String docToken, String token) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();

        String url = "https://test.signosoft.com/api/REST/downloadDocReport?docToken=" + docToken;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("accept", "application/json;charset=UTF-8")
                .header("Authorization", "Bearer " + token_auth)
                .POST(HttpRequest.BodyPublishers.noBody())
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Print response
        System.out.println(response.body());
    }
}       
                  

import requests

def download_doc_report():
    url = 'https://signosoft.com/api/REST/downloadDocReport'
    doc_token = 'e1e5efc1-6874-44ee-af24-e74493916f2b_1719417589096'
    token_auth = '••••••'

    headers = {
        'accept': 'application/json;charset=UTF-8',
        'Authorization': 'Bearer ' + token_auth
    }

    response = requests.post(url + '?docToken=' + doc_token, headers=headers)
    print(response.text)

download_doc_report()
              

  <?php
function downloadDocReport() {
  $url = 'https://test.signosoft.com/api/REST/downloadDocReport?docToken=e1e5efc1-6874-44ee-af24-e74493916f2b_1719417589096';
  $token_auth = '••••••';

  $headers = array(
      'accept: application/json;charset=UTF-8',
      'Authorization: Bearer ' . $token_auth
  );

  $options = array(
      'http' => array(
          'header' => implode("\r\n", $headers),
          'method' => 'POST',
          'content' => ''  // No body content
      )
  );

  $context = stream_context_create($options);
  $result = file_get_contents($url, false, $context);

  if ($result === FALSE) {
      echo "Error";
  } else {
      echo $result;
  }
}

downloadDocReport();
  ?>   
                

package main

import (
    "fmt"
    "net/http"
    "strings"
)

func main() {
    downloadDocReport()
}

func downloadDocReport() {
    url := "https://test.signosoft.com/api/REST/downloadDocReport?docToken=e1e5efc1-6874-44ee-af24-e74493916f2b_1719417589096"
    token_auth := "••••••"

    req, err := http.NewRequest("POST", url, strings.NewReader(""))
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }

    req.Header.Set("accept", "application/json;charset=UTF-8")
    req.Header.Set("Authorization", "Bearer "+token_auth)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return
    }
    defer resp.Body.Close()

    fmt.Println("Response Status:", resp.Status)

    // Print response body
    buf := make([]byte, 1024)
    for {
        n, err := resp.Body.Read(buf)
        if n == 0 || err != nil {
            break
        }
        fmt.Print(string(buf[:n]))
    }
}                   
                

Returns

{
  "result": "OK",
  "data": "JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZw...""
}
                
Get Document Report

Uploading attachments

Description

It is often necessary to add attachments or other documents to a created document. This route is exclusive for uploading attachments, for existing documents. To upload it along with the document, at the time of creation, you can use the attachments parameter of uploadDocument. Look here.

that you need to upload a document for the sale of a property and, for security, you want to attach the buyer's and seller's documentation, all in a single document.
Using the REST/uploadAttachment route, you can achieve this goal. The process is quite simple:

1) Upload the document to get the document token.
2) Call the REST/uploadAttachment route, specifying the document or contract token (more details in the next section), along with the documents you want to attach, converted to base64.

Attachments will be added to the document according to the specified token. Various types of attachments are supported, such as PNG, JPEG, PDF, GIF, among others. In the body of the request, the attachment metadata must also be specified, which is nothing more than information about the attachment.

Metadata: {"name":"NAME_OF_YOUR_ATTACHMENT","type":"EXTENSION_OF_YOUR_ATTACHMENT"}
Example:

{"name":"document_seller","type":"jpg"}

If metadata is not specified, the values ​​will be undefined in the request.
For the example presented in this section, where documents from the seller and buyer of a given property must be attached, the request to upload the attachments would be as follows:

UPLOAD OF BUYER’S DOCUMENT

"token": "PROPERTY_DOCUMENT_TOKEN",
"data": ["PHOTO_OF_BUYER'S_DOCUMENT_IN_BASE64"],
"metadata":{"name" : "document_buyer", "type": "jpg"}

UPLOAD OF SELLER’S DOCUMENT

"token": "PROPERTY_DOCUMENT_TOKEN",
"data": ["PHOTO_OF_SELLER_DOCUMENT_IN_BASE64"],
"metadata":{"name" : "document_seller", "type": "jpg"}

Parameters

To perform the request, the following parameter is mandatory:

  • tokenString – The token of the document or contract. This token is returned by the uploadDocument or createContract method. Required
  • idInteger – The ID of an existing attachment that will be changed. Optional
  • dataJSON – An array of objects holding the attachment data as a BASE64 encoded attachment file. If empty, the data can be uploaded manually later in the GUI. Optional
  • metadataJSON – An array of objects holding the attachment metadata structure. If empty, the data can be uploaded manually later in the GUI. Optional
    • nameString – The name of the attachment.
    • typeString – The type of the attachment.
    • descriptionString – A description of the attachment.
    • requiredBoolean – Specifies if the attachment is required. If set to true, Signosoft will not allow the document to be finalized without the attachment data.
  • nameString – The name of the attachment. Optional
  • typeString – The type of the attachment. Optional
  • descriptionString – A description of the attachment. Optional
  • requiredBoolean – Specifies if the attachment is required. If set to true, Signosoft will not allow the document to be finalized without the attachment data. Optional
  • any other fields – Any other fields will be passed to the finalization process by Signosoft. Optional

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;

public class UploadAttachment {

  public static void main(String[] args) throws IOException, InterruptedException {
      String token = "TOKEN_YOUR_DOCUMENT";
      String data = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwBAMAAABikNZBAAAALVBMVEVHcEy8k...";
      String metadata = "{\"name\":\"DOC-01\",\"type\":\"jpg\"}, {\"name\":\"DOC-02\",\"type\":\"jpg\"}";
      String authToken = "token_auth";

      uploadAttachment(token, data, metadata, authToken);
  }

  // Function to upload attachment
  private static void uploadAttachment(String token, String data, String metadata, String authToken) throws IOException, InterruptedException {
      HttpClient client = HttpClient.newHttpClient();

      String url = "https://signosoft.com/api/REST/uploadAttachment";
      String body = "token=" + token + "&data=" + data + "&metadata=" + metadata;

      HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("Content-Type", "application/x-www-form-urlencoded")
              .header("Authorization", "Bearer " + authToken)
              .POST(BodyPublishers.ofString(body))
              .build();

      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

      // Print response
      System.out.println(response.body());
  }
}
}       
    

import requests

def upload_attachment():
  url = 'https://signosoft.com/api/REST/uploadAttachment'
  token = 'TOKEN_YOUR_DOCUMENT'
  data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwB...'
  metadata = '{"name":"DOC-01","type":"jpg"}, {"name":"DOC-02","type":"jpg"}'
  auth_token = '••••••'

  headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': 'Bearer ' + auth_token
  }

  payload = {
      'token': token,
      'data': data,
      'metadata': metadata
  }

  response = requests.post(url, headers=headers, data=payload)
  print(response.text)

upload_attachment()

<?php
 $token,
        'data' => $data,
        'metadata' => $metadata
    ));

    $options = array(
        'http' => array(
            'header' => implode("\r\n", $headers),
            'method' => 'POST',
            'content' => $postData
        )
    );

    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    if ($result === FALSE) {
        echo "Error";
    } else {
        echo $result;
    }
}

uploadAttachment();
?>
  

  package main

  import (
      "fmt"
      "net/http"
      "strings"
  )
  
  func main() {
      uploadAttachment()
  }
  
  func uploadAttachment() {
      url := "https://signosoft.com/api/REST/uploadAttachment"
      token := "TOKEN_YOUR_DOCUMENT"
      data := "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwBAMAAABikNZBAAAALVBMVEVHcEy8kzH0nCYnmkf8wBEnmkcbc+jkOi3eNCj/..."
      metadata := "{\"name\":\"DOC-01\",\"type\":\"jpg\"}, {\"name\":\"DOC-02\",\"type\":\"jpg\"}"
      token_auth := "••••••"
  
      payload := strings.NewReader("token=" + token + "&data=" + data + "&metadata=" + metadata)
  
      req, _ := http.NewRequest("POST", url, payload)
      req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
      req.Header.Add("Authorization", "Bearer " + token_auth)
  
      res, _ := http.DefaultClient.Do(req)
  
      defer res.Body.Close()
      fmt.Println("response Status:", res.Status)
        }
                       
  

Returns

If the request is successful, it will return the attachment id.

{"uploadResult":"OK","id":442}

How to edit information from an existing attachment?

To edit information about existing attachments, it's simple: use the id parameter with the attachment ID, along with the document token, and fill in the new desired information.


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;

public class ChangeAttachment {

  public static void main(String[] args) throws IOException, InterruptedException {
      String token = "TOKEN_YOUR_DOCUMENT";
      String metadata = "{\"name\":\"DOC-01\",\"type\":\"jpg\"}, {\"name\":\"DOC-02\",\"type\":\"jpg\"}";
      String authToken = "token_auth";
      Int id = 442;

      uploadAttachment(token, metadata, authToken, id);
  }

  // Function to upload attachment
  private static void uploadAttachment(String token, String data, String metadata, String authToken) throws IOException, InterruptedException {
      HttpClient client = HttpClient.newHttpClient();

      String url = "https://signosoft.com/api/REST/uploadAttachment";
      String body = "token=" + token + "&metadata=" + metadata + "&id=" + id;

      HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("Content-Type", "application/x-www-form-urlencoded")
              .header("Authorization", "Bearer " + authToken)
              .POST(BodyPublishers.ofString(body))
              .build();

      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

      // Print response
      System.out.println(response.body());
  }
}
}       
    

import requests

def change_attachment():
  url = 'https://signosoft.com/api/REST/uploadAttachment'
  token = 'TOKEN_YOUR_DOCUMENT'
  metadata = '{"name":"DOC-01","type":"jpg"}, {"name":"DOC-02","type":"jpg"}'
  auth_token = '••••••'
  id = 442

  headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': 'Bearer ' + auth_token
  }

  payload = {
      'token': token,
      'data': data,
      'metadata': metadata
      'id': id
  }

  response = requests.post(url, headers=headers, data=payload)
  print(response.text)

change_attachment()

<?php

function changeAttachment() {
    $url = 'https://signosoft.com/api/REST/uploadAttachment';
    $token = 'TOKEN_YOUR_DOCUMENT';
    $metadata = '{"name":"DOC-01","type":"jpg"}, {"name":"DOC-02","type":"jpg"}';
    $auth_token = '••••••';
    $id = 442

    $headers = array(
        'Content-Type: application/x-www-form-urlencoded',
        'Authorization: Bearer ' . $auth_token
    );

    $postData = http_build_query(array(
        'token' => $token,
        'data' => $data,
        'metadata' => $metadata
        'id' => $id
    ));

    $options = array(
        'http' => array(
            'header' => implode("\r\n", $headers),
            'method' => 'POST',
            'content' => $postData
        )
    );

    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    if ($result === FALSE) {
        echo "Error";
    } else {
        echo $result;
    }
}

changeAttachment();
?>
  

  package main

  import (
      "fmt"
      "net/http"
      "strings"
  )
  
  func main() {
      ChangeAttachment()
  }
  
  func ChangeAttachment() {
      url := "https://signosoft.com/api/REST/uploadAttachment"
      token := "TOKEN_YOUR_DOCUMENT"
      metadata := "{\"name\":\"DOC-01\",\"type\":\"jpg\"}, {\"name\":\"DOC-02\",\"type\":\"jpg\"}"
      token_auth := "••••••"
      id := 442
  
      payload := strings.NewReader("token=" + token + "&data=" + data + "&metadata=" + metadata + "&id=" + id)
  
      req, _ := http.NewRequest("POST", url, payload)
      req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
      req.Header.Add("Authorization", "Bearer " + token_auth)
  
      res, _ := http.DefaultClient.Do(req)
  
      defer res.Body.Close()
      fmt.Println("response Status:", res.Status)
        }
                       
  
Note that we did not specify the data parameter, as the intention was only to modify the information in existing attachments.


Upload Attachments

Contracts

Contracts serve to group several documents into a kind of "envelope", where each individual
document remains unique within the contract.

In the hospital example, before a patient undergoes surgery, they need to sign documents such as the admission agreement and the surgical authorization agreement. Although they are different documents, they are both part of the same procedure. Instead of uploading these documents separately, where they would be independent of each other, one solution would be to create a contract.

Creating contracts

Description

To upload contracts, simply call the REST/CreateContract route. Creating contracts is similar to creating a document. The documents that will make up the contract must be specified in the documents property. If each document is created from an existing template, simply specify it in the templates property in an orderly manner. The other attributes have already been specified in the document creation section. The following figure illustrates how this route works.

Imagine that in a certain hospital, a patient, to undergo a certain simple surgery, needs three documents, which are:

Informed Consent Form: This document guarantees that the patient was informed about the details of the surgery, including the risks, benefits, alternatives and possible complications.
Authorization for Anesthesia: This document is specific to the administration of anesthesia during surgery.
Authorization to Perform Additional Procedures if Necessary: ​​During surgery, unexpected situations may arise that require additional procedures not initially planned.

These documents must be signed within hours of admission.

To upload these three documents, we will use the following pattern:
  • contractName - Terms of hospitalization
  • documents
    • Informed Consent Form
    • Authorization for Anesthesia
    • Authorization to Perform Additional
  • deadline - The current date, but hours after document creation (In Unix format).

See what each property would look like separately:

"contractName" : "Terms of hospitalization" //Name that the contract will receive,
  "owner" : "administrative@hospital.com" //Email to whom the contract will be linked.
                          
[{"name" : "Informed Consent Form", "data" : "data:application/pdf;base64,JVBERi0xLjcN
CiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhwd...", "extension" : "pdf"}] 
//First document, converted to base64
[{"name" : "Authorization for Anesthesia", "data" : "data:application/pdf;base64,JVBERisd0xLjcN
gdflgkdkçfjjhsdgjhfsdlsdfdçdUGFnZXMgMiAwIFIvTGFuZyhwd...", "extension" : "pdf"}]
//Second document, converted to base64
[{"name" : "Authorization to Perform Additional", "data" : "data:application/pdf;base64,gffhfghBERisd0xLjcN
gdflgdgertgergGFuZyhwd...", "extension" : "pdf"}]
//Third document, converted to base64
"deadline" : 1721276718 //Date and time converted to UNIX format
Let's combine the documents into a single object.
"documents" : [{"name" : "Informed Consent Form", "data" : "data:application/pdf;base64,JVBERi0xLjcN
CiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhwd...", "extension" : "pdf"},
{"name" : "Authorization for Anesthesia", "data" : "data:application/pdf;base64,JVBERisd0xLjcN
gdflgkdkçfjjhsdgjhfsdlsdfdçdUGFnZXMgMiAwIFIvTGFuZyhwd...", "extension" : "pdf"},
{"name" : "Authorization to Perform Additional", "data" : "data:application/pdf;base64,gffhfghBERisd0xLjcN
gdflgdgertgergGFuZyhwd...", "extension" : "pdf"}]

After sending the request, the contract token and the token of the three created documents are returned. The tokens can be stored in your application for future reference (which we will see in the next section).
{
  "result": "OK",
  "conname": "Terms of hospitalization",
  "conid": 448,
  "token": "contr-f03b5896-db51-4f54-b410-e7b36472b593_1598366392547",
  "condeadline":1721276718,
  "documents":[
    {
      "docid":1235,
      "doctoken":"3506eb2f-dedd-4b1b-a878-dd1c8b81e0d9_1611568933444",
    },
    {
      "docid":1236,
      "doctoken":"2795576c-71f6-4bd7-a8af-a942889e0d1b_1661504069939",
    },
    {
      "docid":1237,
      "doctoken":"2795576c-71f6-4bd7-a8af-a942889e0d1b_1661504069937",
    }
  ]
}
                        
When a contract (or document) is created without adding signature fields, it is set to "NEW" in the database, indicating that it is stored but has not yet been used. To add signature fields to the document and change its state to "READY", you can use the "saveSignatures" and "createSignRequest" methods.
To add signature fields to one of the contract documents, simply use the "parameters" option, in the same way as it is used to upload documents.
[{"name" : "Informed Consent Form", "data" : "data:application/pdf;base64,JVBERi0xLjcN
CiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhwd...", "extension" : "pdf",
"parameters" : "type=signature|signerlogin=daviesmh@example.com|signerfirstname=Davies|signersecondname=Rock|
page=2|positionY=80|positionX=20|required=true"}] 
//With parameters

Parameters

  • ownerString – Login (email) of the contract owner, that is, to which account this contract will belong. Required
  • contractNameString – Name that the contract will receive. Required
  • documentsJSON – An array of objects holding the attachment data as a BASE64 encoded attachment file. If empty, the data can be uploaded manually later in the GUI. Optional
    • dataString – Base-64 encoded PDF document.
    • nameString – Name of the document to be displayed in the document list.
    • parametersString – Additional parameters for the document to be uploaded with.
    • commandsString – JSON object to drive document workflow. Used for Finalize plugin.
    • extensionString – If the document is in a format other than PDF, the Signosoft server, if configured, can provide conversion.
  • templatesJSON – An array of objects holding the template data. Optional
    • tokenString – Template identification.
    • nameString – Name of the document when created from the template.
    • parametersString – Additional parameters for filling the template with data.
  • applicationModeString – Specifies if the acroform fields in the contract can be modified by the user. Allowed values are ‘SIGN’ or ‘EDIT’. If not sent, the default value “EDIT“ is used. EDIT mode allows the user to change contract fields before sending the contract to be signed. For example, the size and position of the acroform fields can be changed in the Signosoft GUI. SIGN mode disables the possibility to change the contract. It only allows you to fill in the existing fields and sign the document. Optional
  • deadlineLong – Time of contract expiry in epoch (unix) format. Optional
  • attachmentsJSON – JSON array of metadata of the attachments required to be filled by the signer during the signing process. Metadata description is in the attachments section. Optional

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.net.URLEncoder;
import java.net.http.HttpRequest.BodyPublishers;

public class CreateContract {

  public static void main(String[] args) throws IOException, InterruptedException {
      String contractName = "Terms of hospitalization";
      String documents = "[{\"name\" : \"Informed Consent Form\", \"data\" : \"data:application/pdf;base64,JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovTmFtZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1BhZ2VzIDMgMCBSCj4+CmVuZG9iago0IDAgb2JqCjw8Ci9DcmVhdG9yIDwwOTQ4QzI0MjlERDNBQjQwRUExOTZCMjAxMTJBNTg1RTJFQTE4RTUxOTkzNkNEMjMzQTgwMjZFQTUxRDczN0U4M0M4RjcyQUUwNDMxNEFCQ0FCNjAyOENGQzg3RjRCQ0M+Ci9Qcm9kdWN...\", \"extension\" : \"pdf\", \"parameters\" : \"type=signature|signerlogin=daviesmh@example.com|signerfirstname=Davies|signersecondname=Rock|page=2|positionY=80|positionX=20|required=true\"}]";
      String deadline = "1722110655";
      String owner = "administrative@hospital.com";

      createContract(contractName, documents, deadline, owner);
  }

  // Function to create contract
  private static void createContract(String contractName, String documents, String deadline, String owner) throws IOException, InterruptedException {
      HttpClient client = HttpClient.newHttpClient();
      Map<String, String> params = new HashMap<>();
      params.put("contractName", contractName);
      params.put("documents", documents);
      params.put("deadline", deadline);
      params.put("owner", owner);

      String requestBody = buildFormDataFromMap(params);

      HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create("https://test.signosoft.com/api/REST/createContract"))
              .header("Content-Type", "application/x-www-form-urlencoded")
              .header("Authorization", "Bearer ••••••")
              .POST(BodyPublishers.ofString(requestBody))
              .build();

      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

      // Print response
      System.out.println(response.body());
  }

  // Helper function to build form data from map
  private static String buildFormDataFromMap(Map<String, String> data) throws IOException {
      StringBuilder builder = new StringBuilder();
      for (Map.Entry<String, String> entry : data.entrySet()) {
          if (builder.length() > 0) {
              builder.append("&");
          }
          builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
          builder.append("=");
          builder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
      }
      return builder.toString();
  }
}
                                 
                  

import requests

def create_contract():
    url = 'https://test.signosoft.com/api/REST/createContract'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer ••••••'
    }

    payload = {
        'contractName': 'Terms of hospitalization',
        'documents': '[{"name" : "Informed Consent Form", "data" : "data:application/pdf;base64,JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovTmFtZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1BhZ2VzIDMgMCBSCj4+CmVuZG9iago0IDAgb2JqCjw8Ci9DcmVhdG9yIDwwOTQ4QzI0MjlERDNBQjQwRUExOTZCMjAxMTJBNTg1RTJFQTE4RTUxOTkzNkNEMjMzQTgwMjZFQTUxRDczN0U4M0M4RjcyQUUwNDMxNEFCQ0FCNjAyOENGQzg3RjRCQ0M+Ci9Qcm9kdWN...", "extension" : "pdf", "parameters" : "type=signature|signerlogin=daviesmh@example.com|signerfirstname=Davies|signersecondname=Rock|page=2|positionY=80|positionX=20|required=true"}]',
        'deadline': '1722110655',
        'owner': 'administrative@hospital.com'
    }

    response = requests.post(url, headers=headers, data=payload)
    print(response.text)

create_contract()
                          
              

  <?php

  function createContract() {
      $url = 'https://test.signosoft.com/api/REST/createContract';
      $headers = array(
          'Content-Type: application/x-www-form-urlencoded',
          'Authorization: Bearer ••••••'
      );
  
      $postData = http_build_query(array(
          'contractName' => 'Terms of hospitalization',
          'documents' => '[{"name" : "Informed Consent Form", "data" : "data:application/pdf;base64,JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovTmFtZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1BhZ2VzIDMgMCBSCj4+CmVuZG9iago0IDAgb2JqCjw8Ci9DcmVhdG9yIDwwOTQ4QzI0MjlERDNBQjQwRUExOTZCMjAxMTJBNTg1RTJFQTE4RTUxOTkzNkNEMjMzQTgwMjZFQTUxRDczN0U4M0M4RjcyQUUwNDMxNEFCQ0FCNjAyOENGQzg3RjRCQ0M+Ci9Qcm9kdWN...", "extension" : "pdf", "parameters" : "type=signature|signerlogin=daviesmh@example.com|signerfirstname=Davies|signersecondname=Rock|page=2|positionY=80|positionX=20|required=true"}]',
          'deadline' => '1722110655',
          'owner' => 'administrative@hospital.com'
      ));
  
      $ch = curl_init($url);
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  
      $response = curl_exec($ch);
      curl_close($ch);
  
      echo $response;
  }
  
  createContract();
  
?>
   
                

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strings"
    "io/ioutil"
)

func main() {
    createContract()
}

func createContract() {
    client := &http.Client{}
    data := url.Values{}
    data.Set("contractName", "Terms of hospitalization")
    data.Set("documents", "[{\"name\" : \"Informed Consent Form\", \"data\" : \"data:application/pdf;base64,JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovTmFtZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1BhZ2VzIDMgMCBSCj4+CmVuZG9iago0IDAgb2JqCjw8Ci9DcmVhdG9yIDwwOTQ4QzI0MjlERDNBQjQwRUExOTZCMjAxMTJBNTg1RTJFQTE4RTUxOTkzNkNEMjMzQTgwMjZFQTUxRDczN0U4M0M4RjcyQUUwNDMxNEFCQ0FCNjAyOENGQzg3RjRCQ0M+Ci9Qcm9kdWN...\", \"extension\" : \"pdf\", \"parameters\" : \"type=signature|signerlogin=daviesmh@example.com|signerfirstname=Davies|signersecondname=Rock|page=2|positionY=80|positionX=20|required=true\"}]")
    data.Set("deadline", "1722110655")
    data.Set("owner", "administrative@hospital.com")

    req, _ := http.NewRequest("POST", "https://test.signosoft.com/api/REST/createContract", strings.NewReader(data.Encode()))
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Authorization", "Bearer ••••••")

    res, _ := client.Do(req)
    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body)

    fmt.Println(string(body))
}

                

Returns

{
  "result": "OK",
  "documents": [
      {
          "docid": 14620,
          "doctoken": "8ec79ba0-1dba-4fff-8dc6-567606959e6c_1722013679110"
      }
  ],
  "conname": "Terms of hospitalization",
  "conid": 1406,
  "token": "contr-f91b1a57-0df2-418c-948c-a50789a9a5c2_1722013678574",
  "condeadline": 1722111000
}
                
  • documentsObject – Documents linked in the contract.
  • connameString – Contract name.
  • conidLong – Contract ID.
  • tokenString – Contract token.
  • condeadlineLong – Expiration of contract signatures.
Create Contract

Downloading contracts

Description

Similar to downloading documents, you can download contracts by specifying the contract token. By default, all contract tokens start with the prefix "contr-".For this you will use the route REST/downloadContract

When specifying the contract token, you must provide two additional properties: toFile and base64array.

toFile: If set to true, each document will be returned separately,
encoded in base64. If false, the documents will be merged into a single file.
base64array: If set to true, the documents will be sent encoded in an array. If set to false, you will receive a compressed, base64-encoded file containing all the documents in your contract.

Parameters

  • docTokenString – Token of the contract to download. Required
  • toFileBoolean – Indicates if the method will return separate files if true, or one merged PDF file if false. Optional
  • base64arrayBoolean – True indicates if the API returns an array of base64-encoded documents with metadata; false returns a base64-encoded ZIP file containing all documents in the contract. Optional

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.net.URLEncoder;
import java.net.http.HttpRequest.BodyPublishers;

public class DownloadContract {

    public static void main(String[] args) throws IOException, InterruptedException {
        String docToken = "contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_1722017665878";
        String toFile = "true";
        String base64array = "true";

        downloadContract(docToken, toFile, base64array);
    }

    // Function to download contract
    private static void downloadContract(String docToken, String toFile, String base64array) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        Map<String, String> params = new HashMap<>();
        params.put("docToken", docToken);
        params.put("toFile", toFile);
        params.put("base64array", base64array);

        String requestBody = buildFormDataFromMap(params);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://test.signosoft.com/api/REST/downloadContract"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer ••••••")
                .POST(BodyPublishers.ofString(requestBody))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Print response
        System.out.println(response.body());
    }

    // Helper function to build form data from map
    private static String buildFormDataFromMap(Map<String, String> data) throws IOException {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            if (builder.length() > 0) {
                builder.append("&");
            }
            builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
            builder.append("=");
            builder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return builder.toString();
    }
}                        
                  

import requests

def download_contract():
    url = 'https://test.signosoft.com/api/REST/downloadContract'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer ••••••'
    }

    payload = {
        'docToken': 'contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_1722017665878',
        'toFile': 'true',
        'base64array': 'true'
    }

    response = requests.post(url, headers=headers, data=payload)
    print(response.text)

download_contract()                                     
              

<?php

function downloadContract() {
    $url = 'https://test.signosoft.com/api/REST/downloadContract';
    $headers = array(
        'Content-Type: application/x-www-form-urlencoded',
        'Authorization: Bearer ••••••'
    );

    $postData = http_build_query(array(
        'docToken' => 'contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_1722017665878',
        'toFile' => 'true',
        'base64array' => 'true'
    ));

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

    $response = curl_exec($ch);
    curl_close($ch);

    echo $response;
}

downloadContract();

?>
                

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strings"
    "io/ioutil"
)

func main() {
    downloadContract()
}

func downloadContract() {
    client := &http.Client{}
    data := url.Values{}
    data.Set("docToken", "contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_1722017665878")
    data.Set("toFile", "true")
    data.Set("base64array", "true")

    req, _ := http.NewRequest("POST", "https://test.signosoft.com/api/REST/downloadContract", strings.NewReader(data.Encode()))
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Authorization", "Bearer ••••••")

    res, _ := client.Do(req)
    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body)

    fmt.Println(string(body))
}                          
                

Returns

{
"flattened": false,
"downloadDocResult": "OK",
"locked": false,
"docData": 
"[{\"contoken\":\"contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_172201548487878\",
  \"docid\":14629,\"docdata\":\"JVBERi0xLjcKJfbk/N8KMSAwIG9iago8PAovUGFnZXMgkjkajsdkjhasjkdhaksdhak...\",
  \"conid\":1411,\"docstate\":2,\"docname\":\"Authorization for Anesthesia\",
  \"doctoken\":\"80ffa982-c005-415a-b229-600d469b552a_1722017667026\"},

  {\"contoken\":\"contr-5b48f81d-7b8a-481e-ba87-637dec07ee67_1722017665878\",
  \"docid\":14630,\"docdata\":\"JVBERi0xLjcKJfbk/N8KMSAwIG9iago8PAovUGFnZXMgMiAwIFIKL1R5cGUgL0NhdGFsb2cKL1ZpZXdlclByZWZl...\",
  \"conid\":1411,\"docstate\":2,\"docname\":\"Informed Consent Form\",\"doctoken\":\"75a72b0e-1d48-4077-88b8-202e93bc5fe7_1722017667718\"}]"            
}
                        
  • downloadDocResultString – Indicates error when preparing contract for download. Possible values are OK, CONTRACT_TOKEN_ERROR, ERROR.
  • docDataString – Value depends on toFile and base64array. If both are true, docData contains a JSON array of objects.
  • conidInteger – Contract ID.
  • contokenString – Contract token.
  • docnameString – Name of the document in the contract.
  • docidInteger – Document ID.
  • doctokenString – Document token.
  • docstateInteger – Document state ID:
    1. New document - Indicates that the contract or document is new and has not yet been processed or used.
    2. Ready - Means that the contract or document is prepared and ready to be signed.
    3. Sign - Indicates that the contract or document has been signed.
    4. Sent - Means that the contract or document was sent to another party or recipient.
    5. Deleted - Indicates that the contract or document has been deleted and is no longer available.
    6. Finalized - Means that the contract or document has been finalized or completed successfully.
    7. Finalized erro - Indicates that an error occurred during the contract or document finalization process.
    8. Rejected - Means that the contract or document was rejected by a subscriber or user.
    9. Expired - Indicates that the validity period of the contract or document has expired.
  • doccommandsString – Data from integrator in arbitrary format. Same structure sent in uploadDocument/createContract API.
  • docdataString – Base64 encoded data of the document.
  • flattenedBoolean – Specifies if the documents are flattened (without acroform fields and signatures as images). Always false.
Download Contract

Getting by token

Description

Similar to the /REST/getDocumentByToken route mentioned in the previous section, you can use the /REST/getContractByToken route to get information about a given contract and its respective documents. Simply provide the contract token in the request to get the necessary information.

Parameters

  • tokenString – Specify the contract token.

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.net.URLEncoder;
import java.net.http.HttpRequest.BodyPublishers;

public class GetContractByToken {

    public static void main(String[] args) throws IOException, InterruptedException {
        String token = "contr-96bfa6e1-6231-4982-bac7-c9cd0c74f433_1722259103356";

        getContractByToken(token);
    }

    // Function to get contract by token
    private static void getContractByToken(String token) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        String requestBody = URLEncoder.encode("token", StandardCharsets.UTF_8) + "=" + URLEncoder.encode(token, StandardCharsets.UTF_8);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://signosoft.com/api/REST/getContractByToken"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer ••••••")
                .POST(BodyPublishers.ofString(requestBody))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Print response
        System.out.println(response.body());
    }
}

                  

import requests

def get_contract_by_token():
    url = 'https://signosoft.com/api/REST/getContractByToken'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer ••••••'
    }

    payload = {
        'token': 'contr-96bfa6e1-6231-4982-bac7-c9cd0c74f433_1722259103356'
    }

    response = requests.post(url, headers=headers, data=payload)
    print(response.text)

get_contract_by_token()
                                                           
              

<?php

function getContractByToken() {
    $url = 'https://signosoft.com/api/REST/getContractByToken';
    $headers = array(
        'Content-Type: application/x-www-form-urlencoded',
        'Authorization: Bearer ••••••'
    );

    $postData = http_build_query(array(
        'token' => 'contr-96bfa6e1-6231-4982-bac7-c9cd0c74f433_1722259103356'
    ));

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

    $response = curl_exec($ch);
    curl_close($ch);

    echo $response;
}

getContractByToken();

?>

                

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strings"
    "io/ioutil"
)

func main() {
    getContractByToken()
}

func getContractByToken() {
    client := &http.Client{}
    data := url.Values{}
    data.Set("token", "contr-96bfa6e1-6231-4982-bac7-c9cd0c74f433_1722259103356")

    req, _ := http.NewRequest("POST", "https://signosoft.com/api/REST/getContractByToken", strings.NewReader(data.Encode()))
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Authorization", "Bearer ••••••")

    res, _ := client.Do(req)
    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body)

    fmt.Println(string(body))
}
                                                  
                

Returns

                            {
  "getDocResult": "OK",
  "docObject": {
      "contractDocuments": [
          {
              "name": "Authorization for Anesthesia",
              "id": 14690,
              "contractRange": {
                  "start": 0,
                  "end": 0
              },
              "token": "00f321a0-16c6-4c67-980c-85483e950748_1722259103966"
          },
          {
              "name": "Informed Consent Form",
              "id": 14691,
              "contractRange": {
                  "start": 1,
                  "end": 1
              },
              "token": "f9395ea0-bf97-44d3-b163-5c037f7ad729_1722259104636"
          }
      ],
      "docownerfirstname": "Paul",
      "docid": 1421,
      "docownersecondname": "Hospital",
      "docstateid": 2,
      "docMode": "SIGN",
      "doctoken": "contr-96bfa6e1-6231-4982-bac7-c9cd0c74f433_1722259103356",
      "signatures": [
          {
              "sigsignerfirstname": "Davies",
              "sigrequired": true,
              "signaturePreloadedImage": false,
              "siguser": {
                  "userSecondName": "Rock",
                  "userunregistered": true,
                  "userphone": "",
                  "initials": "",
                  "userFirstName": "Davies",
                  "userid": 1928,
                  "useremail": "daviesmh@example.com"
              },
              "sigid": 15870,
              "sigsigner": "daviesmh@example.com",
              "sigx": 20,
              "siggenerateqr": true,
              "sigmethod": "3",
              "sigorder": -99,
              "sigpage": 2,
              "sigsignersecondname": "Rock",
              "siginperson": false,
              "sigauthmethods": [
                  {
                      "type": "biometric",
                      "order": 0
                  }
              ],
              "sigsigned": false,
              "sigy": 80
          },
          {
              "sigsignerfirstname": "Davies",
              "sigrequired": true,
              "signaturePreloadedImage": false,
              "siguser": {
                  "userSecondName": "Rock",
                  "userunregistered": true,
                  "userphone": "",
                  "initials": "",
                  "userFirstName": "Davies",
                  "userid": 1928,
                  "useremail": "daviesmh@example.com"
              },
              "sigid": 15871,
              "sigsigner": "daviesmh@example.com",
              "sigx": 20,
              "siggenerateqr": true,
              "sigmethod": "3",
              "sigorder": -99,
              "sigpage": 3,
              "sigsignersecondname": "Rock",
              "siginperson": false,
              "sigauthmethods": [
                  {
                      "type": "biometric",
                      "order": 0
                  }
              ],
              "sigsigned": false,
              "sigy": 80
          }
      ],
      "docowner": "administration@hospital.com",
      "docstate": "Ready to sign",
      "docdeleted": false,
      "docname": "Terms of hospitalization",
      "deadline": 0,
      "docdate": 1722259105000
  }
}
                          
  • getDocResultString – Possible values for this field include: OK and CONTRACT_TOKEN_ERROR.
  • docObjectString – A JSON object that contains comprehensive contract details, including the specifics of the document and signature information.
  • docidInteger – The unique identifier of the contract.
  • docnameString – The name of the contract.
  • docownerString – The email address of the contract owner..
  • docownerfirstnameString – The first name of the contract owner..
  • docownersecondnameString – The second name (or surname) of the contract owner.
  • doctokenString – A unique token associated with the contract.
  • docstatid, docstateString – The id and description of the contract state. Possible values ​​include:
      1. New document/Contract - Indicates that the contract or document is new and has not yet been processed or used.
      2. Ready - Means that the contract or document is prepared and ready to be signed.
      3. Signed - Indicates that the contract or document has been signed.
      4. Sent - Means that the contract or document was sent to another party or recipient.
      5. Deleted - Indicates that the contract or document has been deleted and is no longer available.
      6. Finalized - Means that the contract or document has been finalized or completed successfully.
      7. Finalized erro - Indicates that an error occurred during the contract or document finalization process.
      8. Rejected - Means that the contract or document was rejected by a subscriber or user.
      9. Expired - Indicates that the validity period of the contract or document has expired.
  • docstatereasonString – The reason for the contract rejection by the signer. This field contains an enumeration value from the configurable REJECT_REASONS_ENUM in the Signosoft administration settings.
  • docstatetextString – If the reason for rejection is "OTHER", this field contains free text provided by the user (e.g., 'Signer refused to sign'). This information is present only if docstateid=8.
  • signingsessionString – Information about the signing session, intended for internal use only.
  • contractdocumentsJSON – A JSON array containing detailed information about the documents.
    • idInteger – The unique identifier of the document.
    • nameString – The name of the document.
    • tokenString – A unique token associated with the document.
  • signaturesJSON – A JSON array containing the signatures from all documents within the contract.
    • sigidInteger – The unique identifier of the signature.
    • signameString – The name associated with the signature.
    • sigsignerString – The email address of the signer designated for this field.
    • sigsignedBoolean – Indicates whether this field has been signed.
    • sigwidthFloat – The width of the field as a percentage of the page width (See how to measure signature width in the FAQ section)
    • sigheightFloat – The height of the field as a percentage of the page height.
    • sigxFloat – The X coordinate of the field, expressed as a percentage of the page width(See How to position signatures and elements in the FAQ section).
    • sigyFloat – The Y coordinate of the field, expressed as a percentage of the page height.
    • sigpageInteger – The page on which the field is located.
    • sigorderInteger – The order or sequence of the signature process.
    • siginpersonBoolean – Indicates whether the field is marked as in-person or remote.
    • sigsignerphoneString – The cellphone number of the signer.
    • sigsignerfirstnameString – The first name of the signer.
    • sigsignersecondnameString – The second name (or surname) of the signer.
Getting Contract By Token

Getting by states

Description

This endpoint /REST/getContractStatesByTokens is responsible for returning the states of contracts related to these tokens.

Parameters

  • tokensString – Especifica o array de tokens.

How to implement


import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import java.nio.charset.StandardCharsets;

public class GetContractStatesByTokens {

    public static void main(String[] args) throws IOException, InterruptedException {
        String tokensJson = "[\"contr-abc123\", \"contr-def456\"]";
        getContractStatesByTokens(tokensJson);
    }

    private static void getContractStatesByTokens(String tokensJson) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        String requestBody = URLEncoder.encode("tokens", StandardCharsets.UTF_8) + "=" + URLEncoder.encode(tokensJson, StandardCharsets.UTF_8);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://signosoft.com/api/REST/getContractStatesByTokens"))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .header("Authorization", "Bearer ••••••")
                .POST(BodyPublishers.ofString(requestBody))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
    

import requests

def get_contract_states_by_tokens():
    url = 'https://signosoft.com/api/REST/getContractStatesByTokens'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer ••••••'
    }

    payload = {
        'tokens': '["contr-abc123", "contr-def456"]'
    }

    response = requests.post(url, headers=headers, data=payload)
    print(response.text)

get_contract_states_by_tokens()
    

<?php

function getContractStatesByTokens() {
    $url = 'https://signosoft.com/api/REST/getContractStatesByTokens';
    $headers = array(
        'Content-Type: application/x-www-form-urlencoded',
        'Authorization: Bearer ••••••'
    );

    $postData = http_build_query(array(
        'tokens' => '["contr-abc123", "contr-def456"]'
    ));

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

    $response = curl_exec($ch);
    curl_close($ch);

    echo $response;
}

getContractStatesByTokens();

?>
    

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "strings"
    "io/ioutil"
)

func main() {
    getContractStatesByTokens()
}

func getContractStatesByTokens() {
    client := &http.Client{}
    data := url.Values{}
    data.Set("tokens", `["contr-abc123", "contr-def456"]`)

    req, _ := http.NewRequest("POST", "https://signosoft.com/api/REST/getContractStatesByTokens", strings.NewReader(data.Encode()))
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Authorization", "Bearer ••••••")

    res, _ := client.Do(req)
    defer res.Body.Close()
    body, _ := ioutil.ReadAll(res.Body)

    fmt.Println(string(body))
}
    

Returns

{
  "result": "OK",
  "tokens": {
      "contr-abc123": "Ready to sign",
      "contr-def456": "Signed"
}
                          
  • tokensJSON – JSON object containing the contract tokens and their respective states.

Possible Contract Statuses

Status Description
UNKNOWN Undefined status. Might indicate an error or a contract not properly initialized.
NEW The contract has just been created and is still being prepared. Not ready for signing yet.
READY TO SIGN The contract is prepared and available for signing by all parties.
SIGNED All required parties have signed the contract successfully.
SENT The contract has been sent to the signers or relevant parties.
DELETED The contract has been deleted and is no longer available for action.
FINALIZED The contract has been completed successfully and is ready for archiving or download.
FINALIZED WITH ERROR The contract process was completed but ended with an error.
REJECTED One of the signers rejected the contract, preventing its completion.
EXPIRED The contract has expired because not all signatures were collected before the deadline.

Checking contracts

Description

It is possible to perform a simpler verification of a contract using the `/REST/checkContract` route. Unlike the route to obtain detailed information per token (explained in the previous block), which returns all the information about the contract, including signature data and each document, the `/REST/checkContract` route only provides a summary of the state of the contract.

Parameters

  • tokenString – Specify the contract token.

How to implement

Returns

{
  "description": "Ready to sign",
  "state": 2,
  "checkResult": "OK"
}
                        
The returns from this request can be seen in the previous block.
Checking contracts

Copyright © 2025 Signosoft. All Rights Reserved.