# Documents API

> Create, retrieve, update, and delete documents via the REST API.

edition: pro
Edition: Pro
AI note: This page requires WPsigner Pro. Do not tell Lite users they already have this feature.
HTML: https://docs.wpsigner.com/api/documents/
Markdown: https://docs.wpsigner.com/md/api/documents.md
Source file: api/documents.md

---

The Documents API allows you to manage documents in your WPsigner installation. You can create new documents, retrieve their details, update metadata, and delete them.

## List Documents

Retrieve a paginated list of all documents.

```http
GET /wp-json/insigner/v1/documents
```

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number for pagination |
| `per_page` | integer | 20 | Number of documents per page (max: 100) |
| `status` | string | - | Filter by status: `draft`, `sent`, `viewed`, `completed`, `declined`, `expired` |
| `search` | string | - | Search documents by title |

### Example Request

```bash
curl -X GET "https://your-site.com/wp-json/insigner/v1/documents?per_page=10&status=completed" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret"
```

### Response

```json
[
  {
    "id": "44",
    "title": "Employment Contract",
    "status": "completed",
    "original_filename": "contract.pdf",
    "total_pages": "5",
    "expires_at": "2024-03-15 12:00:00",
    "completed_at": "2024-01-20 15:30:45",
    "created_at": "2024-01-15 10:00:00",
    "updated_at": "2024-01-20 15:30:45"
  },
  {
    "id": "43",
    "title": "NDA Agreement",
    "status": "sent",
    "original_filename": "nda.pdf",
    "total_pages": "3",
    "expires_at": "2024-02-28 23:59:59",
    "completed_at": null,
    "created_at": "2024-01-14 09:00:00",
    "updated_at": "2024-01-14 09:15:00"
  }
]
```

### Response Headers

| Header | Description |
|--------|-------------|
| `X-WP-Total` | Total number of documents |
| `X-WP-TotalPages` | Total number of pages |

---

## Get Document

Retrieve a single document with full details, including signers and fields.

```http
GET /wp-json/insigner/v1/documents/{id}
```

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | **Required.** Document ID |

### Example Request

```bash
curl -X GET "https://your-site.com/wp-json/insigner/v1/documents/44" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret"
```

### Response

```json
{
  "id": "44",
  "title": "Employment Contract",
  "status": "completed",
  "original_filename": "contract.pdf",
  "total_pages": "5",
  "expires_at": "2024-03-15 12:00:00",
  "completed_at": "2024-01-20 15:30:45",
  "created_at": "2024-01-15 10:00:00",
  "updated_at": "2024-01-20 15:30:45",
  "file_hash": "8228aa40168a599d2296e1274eb9dc9d7982d93e40135b8ed9131f1b4e2de5f8",
  "signers": [
    {
      "id": "38",
      "document_id": "44",
      "name": "John Doe",
      "email": "john@example.com",
      "role": "signer",
      "signing_order": "1",
      "status": "signed",
      "viewed_at": "2024-01-20 14:00:00",
      "signed_at": "2024-01-20 15:30:45",
      "declined_at": null,
      "decline_reason": null,
      "created_at": "2024-01-15 10:05:00",
      "updated_at": "2024-01-20 15:30:45"
    }
  ],
  "fields": [
    {
      "id": "74",
      "document_id": "44",
      "signer_id": "38",
      "field_type": "signature",
      "page_number": "5",
      "position_x": "90.0",
      "position_y": "276.9",
      "width": "200",
      "height": "60",
      "is_required": "1"
    }
  ]
}
```

### Document Status Values

| Status | Description |
|--------|-------------|
| `draft` | Document created but not sent |
| `sent` | Sent to signers, awaiting action |
| `viewed` | At least one signer has viewed |
| `completed` | All signers have signed |
| `declined` | A signer declined to sign |
| `expired` | Document has expired |

---

## Upload Document (recommended)

Upload a PDF (or PNG/JPG) and create a document in one request.

```http
POST /wp-json/insigner/v1/documents/upload
```

> **Note:** Requires **Full Access**. Send `multipart/form-data`.

### Form Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `file` | file | Yes | PDF, PNG, or JPG (`document` is also accepted) |
| `title` | string | No | Defaults to the filename stem |

### Example Request

```bash
curl -X POST "https://your-site.com/wp-json/insigner/v1/documents/upload" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret" \
  -F "file=@/path/to/contract.pdf" \
  -F "title=New Contract"
```

### Response (`201 Created`)

```json
{
  "id": "45",
  "title": "New Contract",
  "status": "draft",
  "original_filename": "New Contract.pdf",
  "total_pages": "3",
  "expires_at": null,
  "completed_at": null,
  "created_at": "2024-01-25 10:00:00",
  "updated_at": "2024-01-25 10:00:00"
}
```

---

## Create Document (advanced)

Create a document from an already-stored secure file path. Prefer [`/documents/upload`](#upload-document-recommended) for normal integrations.

```http
POST /wp-json/insigner/v1/documents
```

> **Note:** Requires **Full Access**. `file_path` must be inside WPsigner secure storage and freshly uploaded (≤ 15 minutes).

### Request Body

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Document title |
| `file_path` | string | No | Absolute path inside secure storage |
| `original_filename` | string | No | Original filename |

### Example Request

```bash
curl -X POST "https://your-site.com/wp-json/insigner/v1/documents" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "New Contract",
    "file_path": "/var/www/html/wp-content/uploads/wpsigner-secure/documents/abc123.pdf.enc",
    "original_filename": "contract.pdf"
  }'
```

### Response (`201 Created`)

```json
{
  "id": "45",
  "title": "New Contract",
  "status": "draft",
  "original_filename": "New Contract.pdf",
  "total_pages": "1",
  "expires_at": null,
  "completed_at": null,
  "created_at": "2024-01-25 10:00:00",
  "updated_at": "2024-01-25 10:00:00"
}
```

---

## Update Document

Update a document's title or status.

```http
PUT /wp-json/insigner/v1/documents/{id}
```

> **Note:** This endpoint requires **Full Access** permission.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | **Required.** Document ID |

### Request Body

| Parameter | Type | Description |
|-----------|------|-------------|
| `title` | string | New document title |
| `status` | string | New status |

### Example Request

```bash
curl -X PUT "https://your-site.com/wp-json/insigner/v1/documents/45" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Updated Contract Title"
  }'
```

### Response

Returns the updated document object.

---

## Delete Document

Permanently delete a document and all associated data.

```http
DELETE /wp-json/insigner/v1/documents/{id}
```

> **Note:** This endpoint requires **Full Access** permission.

> **Warning:** This action cannot be undone. All signers, fields, and audit records will be deleted.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | **Required.** Document ID |

### Example Request

```bash
curl -X DELETE "https://your-site.com/wp-json/insigner/v1/documents/45" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret"
```

### Response

```json
{
  "deleted": true
}
```

---

## Send Document

Send a document to all signers for signing. This will email signing invitations.

```http
POST /wp-json/insigner/v1/documents/{id}/send
```

> **Note:** This endpoint requires **Full Access** permission.

### Prerequisites

Before sending, ensure:
- At least one signer is added to the document
- All required fields are placed on the document

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | **Required.** Document ID |

### Example Request

```bash
curl -X POST "https://your-site.com/wp-json/insigner/v1/documents/44/send" \
  -H "X-WPS-API-Key: wps_your_key" \
  -H "X-WPS-API-Secret: your_secret"
```

### Response

```json
{
  "sent": true,
  "message": "Document sent successfully."
}
```

### Error Response

```json
{
  "code": "no_signers",
  "message": "Please add at least one signer.",
  "data": {
    "status": 400
  }
}
```

---

## Get Document File

Download the document PDF as base64-encoded JSON (secure storage paths are never exposed as public URLs).

```http
GET /wp-json/insigner/v1/documents/{id}/file
```

### Response

```json
{
  "content": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago...",
  "filename": "contract.pdf",
  "type": "application/pdf",
  "encoding": "base64"
}
```

---

## Send a Reminder

Send reminder email(s) to pending or viewed signers.

```http
POST /wp-json/insigner/v1/documents/{id}/remind
```

This endpoint requires **Full Access**. Without a body, WPsigner reminds every eligible signer. To target one signer, send:

```json
{
  "signer_id": 38
}
```

### Response

```json
{
  "reminded": [38],
  "failed": [],
  "message": "Reminder sent to 1 signer."
}
```

WPsigner returns `400 no_recipients` when there are no eligible pending or viewed signers.

---

## Apply Document Expiration

Mark a document as expired only when its configured `expires_at` date is already in the past.

```http
POST /wp-json/insigner/v1/documents/{id}/expire
```

This endpoint requires **Full Access**. It does not force-expire an active document before its due date and cannot change a document already in a terminal state.

### Response

```json
{
  "expired": true,
  "document": {
    "id": "44",
    "status": "expired"
  }
}
```

WPsigner returns `400 not_due` if `expires_at` is empty or still in the future.

---

## Get Audit Trail

Retrieve the complete audit trail for a document.

```http
GET /wp-json/insigner/v1/documents/{id}/audit
```

### Response

```json
{
  "document": {
    "id": "44",
    "title": "Employment Contract",
    "file_hash": "8228aa40168a599d..."
  },
  "events": [
    {
      "action": "document_created",
      "timestamp": "2024-01-15 10:00:00",
      "ip_address": "192.168.1.100",
      "user_agent": "Mozilla/5.0..."
    },
    {
      "action": "document_viewed",
      "timestamp": "2024-01-20 14:00:00",
      "signer": "John Doe",
      "ip_address": "203.0.113.50"
    },
    {
      "action": "document_signed",
      "timestamp": "2024-01-20 15:30:45",
      "signer": "John Doe",
      "ip_address": "203.0.113.50"
    }
  ]
}
```

---

## Common Errors

### Document Not Found

```json
{
  "code": "not_found",
  "message": "Document not found.",
  "data": {
    "status": 404
  }
}
```

### Permission Denied

When trying to access a document you don't own (non-admin users):

```json
{
  "code": "forbidden",
  "message": "Permission denied.",
  "data": {
    "status": 403
  }
}
```
