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
POST /webhooks
Request
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
{
"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:
{
"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
POST /share
Request
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
[
{
"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
GET /documents/:documentId
Request
GET /documents/b026a8da-5719-40eb-bcab-c08c08b1a3dd?token=OhOy3pXNgo31YZwDc6F3IbM1Sypv6oGUVns6h3T8Y67ShU77AgHH53No9F7x4JOi
Response
{
"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.contentin the response.Example value:
oLgTLgXz9EEJGgGHWobmjj7QpgeZDW68hR9jPJbkcpnhGyI4krfnqFj5kIiNJh7K
shareStateName - Located at
results[0].workflow.shareStateNamein 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
GET /files/loadFile/hash/:hash
Request
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.
// 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();