mirror of
https://github.com/documenso/documenso.git
synced 2026-07-09 20:45:00 +10:00
b92c53dbb2
Co-authored-by: Catalin Pit <catalinpit@gmail.com>
520 lines
12 KiB
Plaintext
520 lines
12 KiB
Plaintext
---
|
|
title: First API Call
|
|
description: Create and send a document for signing using the Documenso API, from uploading a PDF to adding recipients and distributing for signature.
|
|
---
|
|
|
|
import { Callout } from 'fumadocs-ui/components/callout';
|
|
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
|
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
|
|
|
## Prerequisites
|
|
|
|
Before starting, you need:
|
|
|
|
- A Documenso account (cloud or self-hosted)
|
|
- An API token ([create one in team settings](/docs/developers/getting-started/authentication))
|
|
- A PDF file to send for signing
|
|
|
|
<Callout type="warn">
|
|
API tokens have full access to your account. Store them securely and never commit them to version
|
|
control.
|
|
</Callout>
|
|
|
|
## 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.
|
|
|
|
<Tabs items={['curl', 'JavaScript']}>
|
|
<Tab value="curl">
|
|
```bash
|
|
curl -X GET "https://app.documenso.com/api/v2/envelope" \
|
|
-H "Authorization: YOUR_API_TOKEN"
|
|
```
|
|
</Tab>
|
|
<Tab value="JavaScript">
|
|
```javascript
|
|
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);
|
|
|
|
````
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
A successful response returns a list of your documents (envelopes):
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
<Tabs items={['curl', 'JavaScript']}>
|
|
<Tab value="curl">
|
|
```bash
|
|
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"
|
|
```
|
|
</Tab>
|
|
<Tab value="JavaScript">
|
|
```javascript
|
|
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);
|
|
````
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
### 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.) |
|
|
|
|
<Callout type="info">
|
|
To place a signature near the bottom-left of the page, use `positionX: 10` and `positionY: 80`.
|
|
</Callout>
|
|
|
|
### 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 |
|
|
|
|
<Callout type="info">
|
|
See the [recipient roles](/docs/concepts/recipient-roles) page for more information.
|
|
</Callout>
|
|
|
|
## 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:
|
|
|
|
<Tabs items={['curl', 'JavaScript']}>
|
|
<Tab value="curl">
|
|
```bash
|
|
curl -X POST "https://app.documenso.com/api/v2/envelope/envelope_abc123/distribute" \
|
|
-H "Authorization: YOUR_API_TOKEN" \
|
|
-H "Content-Type: application/json"
|
|
````
|
|
|
|
</Tab>
|
|
<Tab value="JavaScript">
|
|
```javascript
|
|
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);
|
|
````
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
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:
|
|
|
|
<Tabs items={['JavaScript', 'curl']}>
|
|
<Tab value="JavaScript">
|
|
```javascript
|
|
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);
|
|
````
|
|
|
|
</Tab>
|
|
<Tab value="curl">
|
|
```bash
|
|
#!/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!"
|
|
|
|
````
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
## 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
|
|
|
|
```json
|
|
{
|
|
"error": "Description of what went wrong",
|
|
"code": "ERROR_CODE",
|
|
"statusCode": 400
|
|
}
|
|
````
|
|
|
|
### Common Errors
|
|
|
|
**Invalid file type:**
|
|
|
|
```json
|
|
{
|
|
"error": "Invalid file type. Only PDF files are supported.",
|
|
"statusCode": 400
|
|
}
|
|
```
|
|
|
|
**Missing required field:**
|
|
|
|
```json
|
|
{
|
|
"error": "Recipient email is required",
|
|
"statusCode": 400
|
|
}
|
|
```
|
|
|
|
**Envelope not found:**
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```javascript
|
|
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](https://openapi.documenso.com/) - Full endpoint documentation with request/response schemas
|
|
- [Webhooks](/docs/developers/webhooks/setup) - Get notified when documents are signed
|
|
- [Templates](/docs/developers/api/templates) - Create reusable document templates
|
|
- [SDKs](#official-sdks) - Use typed client libraries
|
|
|
|
### Official SDKs
|
|
|
|
For production applications, consider using the official SDKs:
|
|
|
|
- [TypeScript SDK](https://github.com/documenso/sdk-typescript)
|
|
- [Python SDK](https://github.com/documenso/sdk-python)
|
|
- [Go SDK](https://github.com/documenso/sdk-go)
|