First API Call
Create and send a document for signing using the Documenso API, from uploading a PDF to adding recipients and distributing for signature.
Prerequisites
Before starting, you need:
- A Documenso account (cloud or self-hosted)
- An API token (create one in team settings)
- A PDF file to send for signing
API tokens have full access to your account. Store them securely and never commit them to version control.
Limitations
The API cannot:
- Sign documents on behalf of recipients (recipients must sign themselves)
- Convert non-PDF files to PDF (you must upload PDFs)
- Retrieve the signed PDF until all recipients have completed signing
Base URL
All API requests use the following base URLs:
| Environment | Base URL |
|---|---|
| Production | https://app.documenso.com/api/v2 |
| Staging | https://stg-app.documenso.com/api/v2 |
Example 1: List Your Documents
Start with a simple GET request to verify your API token works.
curl -X GET "https://app.documenso.com/api/v2/envelope" \
-H "Authorization: YOUR_API_TOKEN"A successful response returns a list of your documents (envelopes):
{
"data": [
{
"id": "envelope_abc123",
"status": "DRAFT",
"title": "Contract Agreement",
"createdAt": "2025-01-15T10:30:00.000Z"
}
],
"pagination": {
"page": 1,
"perPage": 10,
"totalPages": 1,
"totalItems": 1
}
}If you receive a 401 Unauthorized error, verify your API token is correct and includes the api_ prefix.
Example 2: Create a Document with Recipient and Signature Field
The V2 API uses a single endpoint to create a document with recipients and fields in one request. This is the most common pattern for sending documents.
curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
-H "Authorization: YOUR_API_TOKEN" \
-H "Content-Type: multipart/form-data" \
-F 'payload={
"type": "DOCUMENT",
"title": "Service Agreement",
"recipients": [
{
"email": "signer@example.com",
"name": "John Smith",
"role": "SIGNER",
"fields": [
{
"identifier": 0,
"type": "SIGNATURE",
"page": 1,
"positionX": 10,
"positionY": 80,
"width": 30,
"height": 5
},
{
"identifier": 0,
"type": "DATE",
"page": 1,
"positionX": 50,
"positionY": 80,
"width": 20,
"height": 3
}
]
}
]
}' \
-F "files=@./contract.pdf;type=application/pdf"Understanding Field Positioning
Field positions use percentage values (0-100) relative to the PDF page dimensions:
| Parameter | Description |
|---|---|
positionX | Horizontal position from left edge (0 = left, 100 = right) |
positionY | Vertical position from top edge (0 = top, 100 = bottom) |
width | Field width as percentage of page width |
height | Field height as percentage of page height |
page | Page number (1-indexed) |
identifier | Index of the file (0 for first file, 1 for second, etc.) |
To place a signature near the bottom-left of the page, use positionX: 10 and positionY: 80.
Recipient Roles
| Role | Description |
|---|---|
SIGNER | Must sign the document |
APPROVER | Must approve before signers can sign |
CC | Receives a copy but doesn't sign |
VIEWER | Can view the document but takes no action |
See the recipient roles page for more information.
Example 3: Send the Document for Signing
After creating a document, it's in DRAFT status. To send it to recipients, use the distribute endpoint:
curl -X POST "https://app.documenso.com/api/v2/envelope/envelope_abc123/distribute" \
-H "Authorization: YOUR_API_TOKEN" \
-H "Content-Type: application/json"After distribution, recipients receive an email with a link to sign the document. The document status changes from DRAFT to PENDING.
Full Workflow Example
Here's a complete script that creates and sends a document:
const fs = require('fs');
const FormData = require('form-data');
const API_TOKEN = process.env.DOCUMENSO_API_TOKEN;
const BASE_URL = 'https://app.documenso.com/api/v2';
async function createAndSendDocument(pdfPath, recipientEmail, recipientName) {
// Step 1: Create the envelope with recipient and fields
const form = new FormData();
const payload = {
type: 'DOCUMENT',
title: 'Service Agreement',
recipients: [
{
email: recipientEmail,
name: recipientName,
role: 'SIGNER',
fields: [
{
identifier: 0,
type: 'SIGNATURE',
page: 1,
positionX: 10,
positionY: 80,
width: 30,
height: 5,
},
{
identifier: 0,
type: 'NAME',
page: 1,
positionX: 10,
positionY: 75,
width: 30,
height: 3,
},
{
identifier: 0,
type: 'DATE',
page: 1,
positionX: 50,
positionY: 80,
width: 20,
height: 3,
},
],
},
],
};
form.append('payload', JSON.stringify(payload));
form.append('files', fs.createReadStream(pdfPath), {
contentType: 'application/pdf',
});
const createResponse = await fetch(`${BASE_URL}/envelope/create`, {
method: 'POST',
headers: {
'Authorization': API_TOKEN,
},
body: form,
});
if (!createResponse.ok) {
const error = await createResponse.json();
throw new Error(`Failed to create envelope: ${JSON.stringify(error)}`);
}
const envelope = await createResponse.json();
console.log('Created envelope:', envelope.id);
// Step 2: Send the document for signing
const distributeResponse = await fetch(
`${BASE_URL}/envelope/${envelope.id}/distribute`,
{
method: 'POST',
headers: {
'Authorization': API_TOKEN,
'Content-Type': 'application/json',
},
}
);
if (!distributeResponse.ok) {
const error = await distributeResponse.json();
throw new Error(`Failed to distribute envelope: ${JSON.stringify(error)}`);
}
console.log('Document sent for signing!');
return envelope.id;
}
// Usage
createAndSendDocument(
'./contract.pdf',
'signer@example.com',
'John Smith'
).catch(console.error);Error Handling
The API returns standard HTTP status codes and JSON error responses:
| Status Code | Meaning |
|---|---|
400 | Bad request - check your request payload |
401 | Unauthorized - invalid or missing API token |
404 | Not found - resource doesn't exist |
429 | Rate limited - wait 60 seconds and retry |
500 | Server error - retry or contact support |
Error Response Format
{
"error": "Description of what went wrong",
"code": "ERROR_CODE",
"statusCode": 400
}Common Errors
Invalid file type:
{
"error": "Invalid file type. Only PDF files are supported.",
"statusCode": 400
}Missing required field:
{
"error": "Recipient email is required",
"statusCode": 400
}Envelope not found:
{
"error": "Envelope not found",
"statusCode": 404
}Handling Rate Limits
The API allows 1000 requests per minute per IP address. Your organisation may have its own lower rate limits. When rate limited, wait at least 60 seconds before retrying:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
console.log('Rate limited, waiting 60 seconds...');
await new Promise((resolve) => setTimeout(resolve, 60000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}Next Steps
- API Reference - Full endpoint documentation with request/response schemas
- Webhooks - Get notified when documents are signed
- Templates - Create reusable document templates
- SDKs - Use typed client libraries
Official SDKs
For production applications, consider using the official SDKs: