Files
documenso/apps/docs/content/docs/developers/getting-started/first-api-call.mdx
T

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 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:
```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)