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