Skip to main content
Skip table of contents

1. Monitoring Document Signing Status

Monitoring Document Signing Status

This scenario demonstrates how to monitor the status of a document signing process using webhooks or polling. Tracking document status is essential for integrations that need to perform actions once a document has been signed or take different paths based on the signing outcome.

Key features:

  • Set up webhooks to receive real-time notifications when documents are signed

  • Share documents for signature with internal or external recipients

  • Check document status through polling as an alternative to webhooks

  • Download the signed document once the signing process is complete

Prerequisites

Before monitoring document signing status, you need:

  • A valid authentication token (see the "Authentication & Security" section for details)

  • A document to share for signature (see the "Document Management" section for details)

  • For webhooks: A publicly accessible URL that can receive POST requests

  • Knowledge of the recipients you want to request signatures from

Webhooks are ideal for real-time notifications and can significantly reduce the need for polling, which helps minimize API usage and improve responsiveness.

Step 1 - Set up a webhook for document events

Webhooks allow you to receive real-time notifications when document events occur, such as when a document is signed or completed. This is a one-time setup that will notify your system about all document events in your organization.

  • Webhooks in Circularo are set at the organization level, not per document

  • The webhook will receive notifications for all document events in your organization

  • The callback URL must be publicly accessible and able to receive POST requests

  • Each notification includes the document ID, event type, actor, and timestamp

Setting up a webhook is a one-time operation. Once configured, the webhook will notify your system about all relevant document events until it is deleted or modified.

Endpoint

CODE
POST /webhooks

Request

JSON
POST /webhooks?token=OhOy3pXNgo31YZwDc6F3IbM1Sypv6oGUVns6h3T8Y67ShU77AgHH53No9F7x4JOi

Content-Type: application/json

{
  "type": "organization",  //Type of webhook - 'organization' monitors all documents in your organization
  "callbackUrl": "https://webhook.site/7d041a97-550a-4e9f-97c9-66547b4807a3"  //URL that will receive webhook notifications
}

Response

JSON
{
  "id": "wbgXxt7wZpNls9IPphlv",  //Unique identifier for the webhook
  "creator": "mary.griffin@circularo.com",
  "createDate": "2023-07-19T10:31:28.570Z",
  "type": "organization",
  "name": "494f4f2f-4215-452c-b004-1bcad4706626",  //Organization ID associated with this webhook
  "callbackUrl": "https://webhook.site/7d041a97-550a-4e9f-97c9-66547b4807a3"
}

The webhook has been successfully created and will now notify your system about document events. When a document is completed (all signatures collected), your callback URL will receive a POST request with a JSON payload similar to this:

JSON
{
  "documentId": "b026a8da-5719-40eb-bcab-c08c08b1a3dd",
  "event": "documentCompleted",
  "actor": "derek.trotter@circularo.com", // The user who performed the last action
  "timestamp": "2023-07-19T10:32:02.872Z"
}

You can use services like webhook.site for testing webhooks during development. For production, ensure your endpoint can handle the expected volume of notifications and implements proper security measures.

Step 2 - Request signature from a recipient

This endpoint shares a document with a recipient and requests their signature. After the document is signed, the webhook (if configured) will notify your system, or you can check the status through polling.

  • The document is shared with the specified recipient for signing

  • A signature field is positioned on the document for the recipient

  • The recipient receives a notification about the signature request

  • The document enters a workflow state waiting for the recipient's signature

Endpoint

CODE
POST /share

Request

JSON
POST /share?token=OhOy3pXNgo31YZwDc6F3IbM1Sypv6oGUVns6h3T8Y67ShU77AgHH53No9F7x4JOi

Content-Type: application/json

{
  "objectType": "document",
  "type": "d_default",
  "id": "b026a8da-5719-40eb-bcab-c08c08b1a3dd",
  "data": [
    {
      "sharePurpose": "sign",
      "shareTo": "derek.trotter@circularo.com"
    }
  ],
  "signatureFields": [
    {
      "user": [
        "derek.trotter@circularo.com"
      ],
      "timestamp": false,
      "required": true,
      "type": "signature",
      "pages": [
        1
      ],
      "position": {
        "percentX": 0.2,
        "percentY": 0.6,
        "percentWidth": 0.4,
        "percentHeight": 0.1
      }
    }
  ]
}

Response

JSON
[
  {
    "shareId": "f76p8hL4BRC1QoiUlImu",
    "isActive": true,
    "isPermanentViewToken": false,
    "shareDate": "2023-07-20T14:04:36.186Z",
    "shareType": "sign",
    "sharedBy": "mary.griffin@circularo.com",
    "sharedObjectEsId": "b026a8da-5719-40eb-bcab-c08c08b1a3dd",
    "sharedObjectEsType": "d_default",
    "sharedObjectType": "document",
    "sharedWith": [
      "derek.trotter@circularo.com"
    ],
    ...
  }
]

The document has been successfully shared with the recipient for signing. The recipient will receive a notification about the signature request.

At this point, the document's share status is "inprogress". Once the recipient signs the document, the status will change to "completed" and your webhook (if configured) will be notified.

For external recipients (those without Circularo accounts), you may want to add protection mechanisms like password or SMS verification.

Step 3 - Check document signing status

This endpoint allows you to check the current status of a document's signing process. You can use this for polling or after receiving a webhook notification to verify the document's state.

  • Retrieves the current status of the document, including its share state

  • Shows whether the signing process is in progress, completed, or in another state

  • Provides access to the document's file hash for downloading the signed document

  • Can be used for polling if webhooks are not configured

This endpoint can be called in two scenarios: 1) After receiving a webhook notification to verify the document's state, or 2) For periodic polling if webhooks are not configured.

It is essential to fetch the document details using this endpoint to get the updated file hash (pdfFile.content) after signing is complete. Using an old file hash would download the original unsigned document instead of the signed version.

Endpoint

CODE
GET /documents/:documentId

Request

JSON
GET /documents/b026a8da-5719-40eb-bcab-c08c08b1a3dd?token=OhOy3pXNgo31YZwDc6F3IbM1Sypv6oGUVns6h3T8Y67ShU77AgHH53No9F7x4JOi

Response

JSON
{
  "results": [
    {
      "documentId": "b026a8da-5719-40eb-bcab-c08c08b1a3dd",
      "documentType": "d_default",
      "isSigned": true,
      "pdfFile": {
        "content": "oLgTLgXz9EEJGgGHWobmjj7QpgeZDW68hR9jPJbkcpnhGyI4krfnqFj5kIiNJh7K",
        ...
      },
      "workflow": {
        "shareStateName": "completed",
        ...
      },
      ...
    }
  ]
}

The response contains the following important properties:

  • pdfFileHash - Located at results[0].pdfFile.content in the response.

    • Example value: oLgTLgXz9EEJGgGHWobmjj7QpgeZDW68hR9jPJbkcpnhGyI4krfnqFj5kIiNJh7K

  • shareStateName - Located at results[0].workflow.shareStateName in the response.

    • The current status of the document share.

    • Example value: completed

The document details have been successfully retrieved, including its current signing status.

  • inprogress: The signing process is active and waiting for recipient action

  • completed: All required signatures have been collected

  • cancelled: The signing process was cancelled by the document owner

  • rejected: The recipient rejected the signature request

  • expired: The signing process has expired without being completed

When using polling instead of webhooks, implement a reasonable polling interval (e.g., every few minutes) to reduce API load.

Step 4 - Download the signed document

Once the document has been signed (status is "completed"), you can download the signed PDF file using the file hash obtained from the document details.

  • Uses the file hash from the document details to download the signed PDF

  • Returns the binary content of the signed document

  • The document includes all applied signatures

  • Can be used to store the signed document in your own system or forward it to other parties

Always fetch the latest document details using the GET /documents/:documentId endpoint before downloading. The file hash changes when the document is signed, and using an outdated hash would result in downloading the unsigned version of the document.

Endpoint

CODE
GET /files/loadFile/hash/:hash

Request

JSON
GET /files/loadFile/hash/oLgTLgXz9EEJGgGHWobmjj7QpgeZDW68hR9jPJbkcpnhGyI4krfnqFj5kIiNJh7K?token=OhOy3pXNgo31YZwDc6F3IbM1Sypv6oGUVns6h3T8Y67ShU77AgHH53No9F7x4JOi

The signed document has been successfully downloaded. The response contains the binary content of the PDF file with all signatures applied.


Monitoring Document Signing Status Summary

You have successfully learned how to monitor document signing status using webhooks and polling, and how to download signed documents once the signing process is complete.

Key Concepts

  • Webhooks: Real-time notifications about document events sent to your callback URL

  • Polling: Periodically checking document status as an alternative to webhooks

  • Share States: Different states a document can be in during the signing process (inprogress, completed, cancelled, rejected, expired)

  • Document Download: Retrieving the signed document once all signatures have been collected

Webhooks vs. Polling

  • Webhooks: More efficient, real-time notifications, reduced API load, requires public endpoint

  • Polling: Simpler implementation, works behind firewalls, higher API load, potential delays in detecting status changes

Next Steps

With your document signing monitoring in place, you can now:

  • Implement business logic based on document signing status

  • Store signed documents in your own system

  • Create more complex workflows with multiple signers and sequential signing

Example Implementation

See our OpenAPI documentation to learn about the full set of API endpoints and parameters.

Please use proper exception handling and function decomposition in your own code. The code is provided for illustrative purposes only and is not intended for production use.

JAVASCRIPT
// Document signing status monitoring example with webhooks
const URL = "https://sandbox.circularo.com";
const API_PATH = "/api/v1";
const TOKEN = "YOUR_AUTH_TOKEN"; // Obtained from login or API key
const DOCUMENT_ID = "YOUR_DOCUMENT_ID"; // ID of the document to monitor
const DOCUMENT_TYPE = "d_default"; // Type of the document
const RECIPIENT_EMAIL = "recipient@example.com"; // Email of the person who should sign
const WEBHOOK_URL = "https://your-server.com/webhook-endpoint"; // Your webhook endpoint URL

// Main implementation
async function main() {
    try {
        // Step 1: Set up a webhook (one-time setup)
        console.log("Setting up webhook for document events...");
        const webhookId = await setupWebhook();
        console.log("Webhook created with ID: ${webhookId}");

        // Step 2: Share document for signature
        console.log("Sharing document ${DOCUMENT_ID} for signature with ${RECIPIENT_EMAIL}...");
        const shareId = await shareDocumentForSignature();
        console.log("Document shared successfully. Share ID: ${shareId}");

        console.log("Webhook is now active and will notify your server when the document is signed.");
        console.log("Your server should implement the webhook endpoint to handle these notifications.");
    } catch (error) {
        console.error('Error in document signing workflow:', error.message);
    }
}

// Set up a webhook for document events
async function setupWebhook() {
    const webhookResponse = await fetch(`${URL}${API_PATH}/webhooks?token=${TOKEN}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            type: "organization",
            callbackUrl: WEBHOOK_URL
        })
    });
    if (!webhookResponse.ok) {
        throw new Error(`Webhook setup failed: ${webhookResponse.status} ${webhookResponse.statusText}`);
    }

    const webhookData = await webhookResponse.json();
    return webhookData.id;
}

// Share document for signature
async function shareDocumentForSignature() {
    const shareResponse = await fetch(`${URL}${API_PATH}/share?token=${TOKEN}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            id: DOCUMENT_ID,
            type: DOCUMENT_TYPE,
            objectType: "document",
            data: [
                {
                    sharePurpose: "sign",
                    shareTo: RECIPIENT_EMAIL
                }
            ],
            signatureFields: [
                {
                    user: [RECIPIENT_EMAIL],
                    timestamp: false,
                    required: true,
                    type: "signature",
                    pages: [1],
                    position: {
                        percentX: 0.2,
                        percentY: 0.6,
                        percentWidth: 0.4,
                        percentHeight: 0.1
                    }
                }
            ]
        })
    });
    if (!shareResponse.ok) {
        throw new Error(`Share failed: ${shareResponse.status} ${shareResponse.statusText}`);
    }

    const shareData = await shareResponse.json();
    return shareData[0].shareId;
}

// Webhook handler (to be implemented on your server)
/*
// Example Express.js webhook handler
app.post('/webhook-endpoint', (req, res) => {
    const webhookData = req.body;

    console.log('Webhook notification received:', webhookData);

    // Check if this is a document completion event for our document
    if (webhookData.event === 'documentCompleted' && webhookData.documentId === DOCUMENT_ID) {
        console.log(`Document ${webhookData.documentId} has been completed!`);

        // Important: Fetch the latest document details to get the updated file hash
        fetchDocumentAndDownload(webhookData.documentId)
            .then(() => console.log('Document processed successfully'))
            .catch(err => console.error('Error processing document:', err));
    }

    // Always respond quickly to webhook calls
    res.status(200).send('Webhook received');
});
*/

// Fetch document details and download if completed
async function fetchDocumentAndDownload(documentId) {
    // Step 1: Get the latest document details
    const statusResponse = await fetch(`${URL}${API_PATH}/documents/${documentId}?token=${TOKEN}`);
    if (!statusResponse.ok) {
        throw new Error(`Status check failed: ${statusResponse.status} ${statusResponse.statusText}`);
    }

    const documentData = await statusResponse.json();
    const shareStatus = documentData.results[0].workflow.shareStateName;
    console.log(`Document signing status: ${shareStatus}`);

    // Step 2: If completed, download the signed document using the updated file hash
    if (shareStatus === "completed") {
        console.log("Document has been signed! Downloading...");
        const fileHash = documentData.results[0].pdfFile.content;
        await downloadSignedDocument(fileHash);
    }

    return shareStatus;
}

// Download the signed document
async function downloadSignedDocument(fileHash) {
    const downloadResponse = await fetch(`${URL}${API_PATH}/files/loadFile/hash/${fileHash}?token=${TOKEN}`);
    if (!downloadResponse.ok) {
        throw new Error(`Download failed: ${downloadResponse.status} ${downloadResponse.statusText}`);
    }

    // For browser environments
    const blob = await downloadResponse.blob();

    // For Node.js environments
    // const buffer = await downloadResponse.buffer();
    // require('fs').writeFileSync('signed-document.pdf', buffer);

    console.log("Signed document downloaded successfully");
    return blob;
}

// Execute the main function
main();
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.