documenso-common-errors
Diagnose and resolve common Documenso API errors and issues. Use when encountering Documenso errors, debugging integration issues, or troubleshooting failed operations. Trigger with phrases like "documenso error", "documenso 401", "documenso failed", "fix documenso", "documenso not working". allowed-tools: Read, Write, Edit, Grep version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>
Allowed Tools
No tools specified
Provided by Plugin
documenso-pack
Claude Code skill pack for Documenso (24 skills)
Installation
This skill is included in the documenso-pack plugin:
/plugin install documenso-pack@claude-code-plugins-plus
Click to copy
Instructions
# Documenso Common Errors
## Overview
Quick reference for diagnosing and resolving common Documenso API errors.
## Prerequisites
- Basic understanding of Documenso SDK
- Access to application logs
- API key available
## Error Reference Table
| Status | Error | Common Cause | Quick Fix |
|--------|-------|--------------|-----------|
| 400 | Bad Request | Invalid parameters | Check request body format |
| 401 | Unauthorized | Invalid/missing API key | Verify DOCUMENSO_API_KEY |
| 403 | Forbidden | Insufficient permissions | Use team API key |
| 404 | Not Found | Resource doesn't exist | Verify ID is correct |
| 409 | Conflict | Duplicate resource | Handle existing resource |
| 422 | Unprocessable | Validation failed | Check field values |
| 429 | Rate Limited | Too many requests | Implement backoff |
| 500 | Server Error | Documenso issue | Retry with backoff |
## Detailed Error Scenarios
### Error: 401 Unauthorized
**Symptoms:**
```
SDKError: Unauthorized
Status: 401
```
**Causes and Solutions:**
```typescript
// Cause 1: Missing API key
// Solution: Set environment variable
export DOCUMENSO_API_KEY="your-api-key"
// Cause 2: Invalid API key format
// Wrong: API key with extra whitespace
const apiKey = " dcs_abc123 "; // Bad
const apiKey = "dcs_abc123"; // Good
// Cause 3: Expired or revoked key
// Solution: Generate new key in Documenso dashboard
// Cause 4: Wrong environment
// Solution: Use correct key for environment
const client = new Documenso({
apiKey: process.env.DOCUMENSO_API_KEY,
serverURL: process.env.DOCUMENSO_BASE_URL, // Match key to URL
});
```
### Error: 403 Forbidden
**Symptoms:**
```
SDKError: Forbidden
Status: 403
```
**Causes and Solutions:**
```typescript
// Cause 1: Personal API key used for team resources
// Solution: Use team API key for team documents
// Get team API key from: Team Settings > API Tokens
// Cause 2: Accessing another user's resource
// Solution: Verify you own the document
const doc = await client.documents.getV0({ documentId });
// Check doc.userId matches your user
// Cause 3: Document in terminal state
// Solution: Cannot modify completed/cancelled documents
if (doc.status === "COMPLETED" || doc.status === "CANCELLED") {
console.log("Cannot modify document in terminal state");
// Create new document instead
}
```
### Error: 404 Not Found
**Symptoms:**
```
SDKError: Not Found
Status: 404
```
**Causes and Solutions:**
```typescript
// Cause 1: Wrong document ID
// Solution: Verify ID format and existence
async function safeGetDocument(documentId: string) {
try {
return await client.documents.getV0({ documentId });
} catch (error: any) {
if (error.statusCode === 404) {
console.log(`Document ${documentId} not found`);
return null;
}
throw error;
}
}
// Cause 2: Document deleted
// Solution: Check if document was deleted
// Cause 3: Using v1 ID with v2 API (or vice versa)
// Solution: IDs have different formats between versions
// v1: numeric IDs like "12345"
// v2: prefixed IDs like "doc_abc123"
```
### Error: 409 Conflict
**Symptoms:**
```
SDKError: Conflict
Status: 409
```
**Causes and Solutions:**
```typescript
// Cause 1: Duplicate recipient email
// Solution: Update existing or skip
async function addOrUpdateRecipient(
documentId: string,
email: string,
name: string
) {
try {
return await client.documentsRecipients.createV0({
documentId,
email,
name,
role: "SIGNER",
});
} catch (error: any) {
if (error.statusCode === 409) {
// Recipient exists - find and update
const doc = await client.documents.getV0({ documentId });
const existing = doc.recipients?.find(r => r.email === email);
if (existing) {
return await client.documentsRecipients.updateV0({
documentId,
recipientId: existing.id!,
name, // Update name
});
}
}
throw error;
}
}
// Cause 2: Template with same name
// Solution: Use unique name or update existing
```
### Error: 422 Unprocessable Entity
**Symptoms:**
```
SDKError: Unprocessable Entity
Status: 422
```
**Causes and Solutions:**
```typescript
// Cause 1: Invalid field position (off-page)
// Solution: Check page dimensions
const fieldConfig = {
page: 1,
positionX: 100, // Must be 0 < x < pageWidth
positionY: 600, // Must be 0 < y < pageHeight
width: 200, // x + width must be < pageWidth
height: 60, // y + height must be < pageHeight
};
// Letter size: 612 x 792 points
// A4 size: 595 x 842 points
// Cause 2: Invalid email format
// Solution: Validate before sending
function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Cause 3: Missing required fields
// Solution: Check all required parameters
const recipient = await client.documentsRecipients.createV0({
documentId, // Required
email, // Required
name, // Required
role: "SIGNER", // Required
});
// Cause 4: Invalid file type
// Solution: Only PDF files are supported
```
### Error: 429 Rate Limited
**Symptoms:**
```
SDKError: Too Many Requests
Status: 429
```
**Solution:**
```typescript
// Implement exponential backoff
async function withBackoff(
operation: () => Promise,
maxRetries = 5
): Promise {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
if (error.statusCode !== 429 || attempt === maxRetries - 1) {
throw error;
}
// Check Retry-After header if available
const retryAfter = error.headers?.["retry-after"];
const delay = retryAfter
? parseInt(retryAfter) * 1000
: Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Waiting ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("Max retries exceeded");
}
// Use queue for bulk operations
import PQueue from "p-queue";
const queue = new PQueue({
concurrency: 2,
interval: 1000,
intervalCap: 5,
});
async function queuedCreate(data: any) {
return queue.add(() => client.documents.createV0(data));
}
```
### Error: 500 Internal Server Error
**Symptoms:**
```
SDKError: Internal Server Error
Status: 500
```
**Solution:**
```typescript
// Retry with exponential backoff (server errors are usually transient)
async function retryServerErrors(
operation: () => Promise
): Promise {
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await operation();
} catch (error: any) {
if (error.statusCode < 500 || attempt === 2) {
throw error;
}
const delay = Math.pow(2, attempt) * 1000;
console.log(`Server error. Retrying in ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("Max retries exceeded");
}
// If persistent, check Documenso status
// https://status.documenso.com
```
## File Upload Errors
```typescript
// Error: File too large
// Solution: Check file size before upload
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
async function uploadWithSizeCheck(pdfPath: string) {
const stats = await fs.promises.stat(pdfPath);
if (stats.size > MAX_FILE_SIZE) {
throw new Error(`File exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`);
}
return openAsBlob(pdfPath);
}
// Error: Invalid file type
// Solution: Verify PDF format
import { fileTypeFromFile } from "file-type";
async function verifyPdf(filePath: string) {
const type = await fileTypeFromFile(filePath);
if (type?.mime !== "application/pdf") {
throw new Error("Only PDF files are supported");
}
}
```
## Webhook Errors
```typescript
// Error: Webhook not receiving events
// Checklist:
// 1. Webhook URL is HTTPS
// 2. Endpoint returns 200 within 30 seconds
// 3. Correct events are subscribed
// 4. Secret is correctly configured
// Error: Signature verification failing
function verifyWebhookSignature(
payload: string,
receivedSecret: string
): boolean {
const expectedSecret = process.env.DOCUMENSO_WEBHOOK_SECRET;
// Documenso sends secret in X-Documenso-Secret header
return receivedSecret === expectedSecret;
}
```
## Debugging Checklist
1. **Check API Key:**
```bash
echo $DOCUMENSO_API_KEY | head -c 10
# Should show: dcs_xxx...
```
2. **Verify Endpoint:**
```bash
curl -H "Authorization: Bearer $DOCUMENSO_API_KEY" \
https://app.documenso.com/api/v2/documents
```
3. **Enable Debug Logging:**
```typescript
const client = new Documenso({
apiKey: process.env.DOCUMENSO_API_KEY,
debugLogger: console,
});
```
4. **Check Document Status:**
```typescript
const doc = await client.documents.getV0({ documentId });
console.log(`Status: ${doc.status}`);
console.log(`Recipients: ${doc.recipients?.length}`);
```
## Output
- Error identified and categorized
- Root cause determined
- Solution implemented
- Retry logic in place
## Resources
- [Documenso API Reference](https://openapi.documenso.com/)
- [Status Page](https://status.documenso.com)
- [GitHub Issues](https://github.com/documenso/documenso/issues)
## Next Steps
For comprehensive debugging, see `documenso-debug-bundle`.