Files
documenso/apps/docs/content/docs/developers/getting-started/first-api-call.mdx
T
Lucas Smith b92c53dbb2 feat: docs v2 (#2460)
Co-authored-by: Catalin Pit <catalinpit@gmail.com>
2026-02-27 22:05:27 +11:00

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)