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"const response = await fetch('https://app.documenso.com/api/v2/envelope', {
method: 'GET',
headers: {
'Authorization': 'YOUR_API_TOKEN',
},
});
const data = await response.json();
console.log(data);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"const fs = require('fs');
const FormData = require('form-data');
const form = new FormData();
const 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,
},
],
},
],
};
form.append('payload', JSON.stringify(payload));
form.append('files', fs.createReadStream('./contract.pdf'), {
contentType: 'application/pdf',
});
const response = await fetch('https://app.documenso.com/api/v2/envelope/create', {
method: 'POST',
headers: {
Authorization: 'YOUR_API_TOKEN',
},
body: form,
});
const data = await response.json();
console.log('Created envelope:', data.id);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"const envelopeId = 'envelope_abc123';
const response = await fetch(
`https://app.documenso.com/api/v2/envelope/${envelopeId}/distribute`,
{
method: 'POST',
headers: {
Authorization: 'YOUR_API_TOKEN',
'Content-Type': 'application/json',
},
},
);
const data = await response.json();
console.log('Document sent:', data);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);#!/bin/bash
set -e
API_TOKEN="YOUR_API_TOKEN"
BASE_URL="https://app.documenso.com/api/v2"
PDF_FILE="./contract.pdf"
RECIPIENT_EMAIL="signer@example.com"
RECIPIENT_NAME="John Smith"
# Step 1: Create the envelope with recipient and fields
echo "Creating envelope..."
ENVELOPE_RESPONSE=$(curl -s -X POST "${BASE_URL}/envelope/create" \
-H "Authorization: ${API_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "payload={
\"type\": \"DOCUMENT\",
\"title\": \"Service Agreement\",
\"recipients\": [
{
\"email\": \"${RECIPIENT_EMAIL}\",
\"name\": \"${RECIPIENT_NAME}\",
\"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=@${PDF_FILE};type=application/pdf")
ENVELOPE_ID=$(echo $ENVELOPE_RESPONSE | jq -r '.id')
echo "Created envelope: ${ENVELOPE_ID}"
# Step 2: Send the document for signing
echo "Sending document..."
curl -s -X POST "${BASE_URL}/envelope/${ENVELOPE_ID}/distribute" \
-H "Authorization: ${API_TOKEN}" \
-H "Content-Type: application/json"
echo "Document sent for signing!"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 100 requests per minute per IP address. 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: