mirror of
https://github.com/documenso/documenso.git
synced 2026-08-25 07:42:28 +10:00
docs(api): document cancel endpoint and fix get-many body shape
- add Cancel Document section: PENDING-only, not idempotent, access rules, webhook and emails - replace fabricated envelopeIds get-many body with the real nested ids selector (max 20) - document the data wrapper and silent filtering of inaccessible IDs - add CANCELLED to status table, state diagram, transitions and filters - replace nonexistent API source value with the real enum; fix pagination shape and fences - warn in migration guide that get-many body shape changed from documentIds
This commit is contained in:
@@ -32,9 +32,9 @@ A document object contains the following properties:
|
|||||||
| --------------- | -------------- | -------------------------------------------------------------- |
|
| --------------- | -------------- | -------------------------------------------------------------- |
|
||||||
| `id` | string | Unique identifier (e.g., `envelope_abc123`) |
|
| `id` | string | Unique identifier (e.g., `envelope_abc123`) |
|
||||||
| `type` | string | `DOCUMENT` or `TEMPLATE` |
|
| `type` | string | `DOCUMENT` or `TEMPLATE` |
|
||||||
| `status` | string | Current status: `DRAFT`, `PENDING`, `COMPLETED`, or `REJECTED` |
|
| `status` | string | Current status: `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED`, or `CANCELLED` |
|
||||||
| `title` | string | Document title |
|
| `title` | string | Document title |
|
||||||
| `source` | string | How the document was created: `DOCUMENT`, `TEMPLATE`, `API` |
|
| `source` | string | How the document was created: `DOCUMENT`, `TEMPLATE`, `TEMPLATE_DIRECT_LINK` |
|
||||||
| `visibility` | string | Who can view: `EVERYONE`, `ADMIN`, `MANAGER_AND_ABOVE` |
|
| `visibility` | string | Who can view: `EVERYONE`, `ADMIN`, `MANAGER_AND_ABOVE` |
|
||||||
| `externalId` | string \| null | Your custom identifier for the document |
|
| `externalId` | string \| null | Your custom identifier for the document |
|
||||||
| `createdAt` | string | ISO 8601 timestamp |
|
| `createdAt` | string | ISO 8601 timestamp |
|
||||||
@@ -53,7 +53,7 @@ A document object contains the following properties:
|
|||||||
"id": "envelope_abc123xyz",
|
"id": "envelope_abc123xyz",
|
||||||
"type": "DOCUMENT",
|
"type": "DOCUMENT",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"source": "API",
|
"source": "DOCUMENT",
|
||||||
"visibility": "EVERYONE",
|
"visibility": "EVERYONE",
|
||||||
"title": "Service Agreement",
|
"title": "Service Agreement",
|
||||||
"externalId": "contract-2025-001",
|
"externalId": "contract-2025-001",
|
||||||
@@ -73,13 +73,13 @@ A document object contains the following properties:
|
|||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"id": "field_123",
|
"id": 123,
|
||||||
"type": "SIGNATURE",
|
"type": "SIGNATURE",
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"positionX": 10,
|
"positionX": "10",
|
||||||
"positionY": 80,
|
"positionY": "80",
|
||||||
"width": 30,
|
"width": "30",
|
||||||
"height": 5,
|
"height": "5",
|
||||||
"recipientId": 1
|
"recipientId": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -99,6 +99,8 @@ A document object contains the following properties:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Field position and size values are stored as decimals and serialized as strings in API responses.
|
||||||
|
|
||||||
## List Documents
|
## List Documents
|
||||||
|
|
||||||
Retrieve a paginated list of documents.
|
Retrieve a paginated list of documents.
|
||||||
@@ -114,7 +116,7 @@ GET /envelope
|
|||||||
| `page` | integer | Page number (default: 1) |
|
| `page` | integer | Page number (default: 1) |
|
||||||
| `perPage` | integer | Results per page (default: 10, max: 100) |
|
| `perPage` | integer | Results per page (default: 10, max: 100) |
|
||||||
| `type` | string | Filter by `DOCUMENT` or `TEMPLATE` |
|
| `type` | string | Filter by `DOCUMENT` or `TEMPLATE` |
|
||||||
| `status` | string | Filter by status: `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED` |
|
| `status` | string | Filter by status: `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED`, `CANCELLED` |
|
||||||
| `source` | string | Filter by creation source |
|
| `source` | string | Filter by creation source |
|
||||||
| `folderId` | string | Filter by folder ID |
|
| `folderId` | string | Filter by folder ID |
|
||||||
| `orderByColumn` | string | Sort field (only `createdAt` supported) |
|
| `orderByColumn` | string | Sort field (only `createdAt` supported) |
|
||||||
@@ -154,8 +156,8 @@ const response = await fetch(`${BASE_URL}/envelope`, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data, pagination } = await response.json();
|
const { data, count } = await response.json();
|
||||||
console.log(`Found ${pagination.totalItems} documents`);
|
console.log(`Found ${count} documents`);
|
||||||
|
|
||||||
// Filter by status
|
// Filter by status
|
||||||
const pendingResponse = await fetch(
|
const pendingResponse = await fetch(
|
||||||
@@ -197,12 +199,10 @@ const pendingDocs = await pendingResponse.json();
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pagination": {
|
"count": 42,
|
||||||
"page": 1,
|
"currentPage": 1,
|
||||||
"perPage": 10,
|
"perPage": 10,
|
||||||
"totalPages": 5,
|
"totalPages": 5
|
||||||
"totalItems": 42
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -628,6 +628,72 @@ The response includes signing URLs for each recipient:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Cancel Document
|
||||||
|
|
||||||
|
Cancel a pending document. This changes its status from `PENDING` to `CANCELLED`.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /envelope/cancel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Body
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
| ------------ | ------ | -------- | ----------------------------------- |
|
||||||
|
| `envelopeId` | string | Yes | Document ID |
|
||||||
|
| `reason` | string | No | Reason for cancelling the document |
|
||||||
|
|
||||||
|
### Code Examples
|
||||||
|
|
||||||
|
<Tabs items={['curl', 'TypeScript']}>
|
||||||
|
<Tab value="curl">
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://app.documenso.com/api/v2/envelope/cancel" \
|
||||||
|
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"envelopeId": "envelope_abc123",
|
||||||
|
"reason": "The agreement is no longer needed."
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
</Tab>
|
||||||
|
<Tab value="TypeScript">
|
||||||
|
```typescript
|
||||||
|
const response = await fetch('https://app.documenso.com/api/v2/envelope/cancel', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
envelopeId: 'envelope_abc123',
|
||||||
|
reason: 'The agreement is no longer needed.',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { success } = await response.json();
|
||||||
|
```
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
- Only documents in `PENDING` status can be cancelled. Other statuses return `400`.
|
||||||
|
- Cancellation is not idempotent. Cancelling the same document again returns `400`.
|
||||||
|
- The document owner and team members with `MANAGER` or higher permissions can cancel it. Requests for documents you cannot view return `404`; requests for visible documents without sufficient permissions return `401`.
|
||||||
|
- A successful cancellation fires the `DOCUMENT_CANCELLED` webhook.
|
||||||
|
- Cancellation emails are sent only to eligible non-CC, non-rejected recipients who were sent or opened the document.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Delete Document
|
## Delete Document
|
||||||
|
|
||||||
Delete a document. Completed documents cannot be deleted.
|
Delete a document. Completed documents cannot be deleted.
|
||||||
@@ -670,7 +736,7 @@ const response = await fetch('https://app.documenso.com/api/v2/envelope/delete',
|
|||||||
|
|
||||||
const { success } = await response.json();
|
const { success } = await response.json();
|
||||||
|
|
||||||
````
|
```
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
@@ -680,7 +746,7 @@ const { success } = await response.json();
|
|||||||
{
|
{
|
||||||
"success": true
|
"success": true
|
||||||
}
|
}
|
||||||
````
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -695,8 +761,10 @@ POST /envelope/get-many
|
|||||||
### Request Body
|
### Request Body
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
| ------------- | ----- | -------- | --------------------- |
|
| ---------- | ------ | -------- | ---------------------------------------------------------------------------- |
|
||||||
| `envelopeIds` | array | Yes | Array of document IDs |
|
| `ids` | object | Yes | ID selector containing `type` and `ids` |
|
||||||
|
| `ids.type` | string | Yes | `envelopeId`, `documentId`, or `templateId` |
|
||||||
|
| `ids.ids` | array | Yes | 1-20 IDs: strings for `envelopeId`; numbers for `documentId` or `templateId` |
|
||||||
|
|
||||||
### Code Examples
|
### Code Examples
|
||||||
|
|
||||||
@@ -707,12 +775,17 @@ curl -X POST "https://app.documenso.com/api/v2/envelope/get-many" \
|
|||||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"envelopeIds": ["envelope_abc123", "envelope_def456", "envelope_ghi789"]
|
"ids": {
|
||||||
|
"type": "envelopeId",
|
||||||
|
"ids": ["envelope_abc123", "envelope_def456", "envelope_ghi789"]
|
||||||
|
}
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab value="TypeScript">
|
<Tab value="TypeScript">
|
||||||
```typescript
|
```typescript
|
||||||
|
const requestedIds = ['envelope_abc123', 'envelope_def456', 'envelope_ghi789'];
|
||||||
|
|
||||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/get-many', {
|
const response = await fetch('https://app.documenso.com/api/v2/envelope/get-many', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -720,16 +793,36 @@ const response = await fetch('https://app.documenso.com/api/v2/envelope/get-many
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
envelopeIds: ['envelope_abc123', 'envelope_def456', 'envelope_ghi789'],
|
ids: {
|
||||||
|
type: 'envelopeId',
|
||||||
|
ids: requestedIds,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const documents = await response.json();
|
const { data } = await response.json();
|
||||||
|
|
||||||
````
|
```
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": "envelope_abc123",
|
||||||
|
"type": "DOCUMENT",
|
||||||
|
"status": "PENDING",
|
||||||
|
"title": "Service Agreement"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint silently omits envelopes you cannot access instead of returning `404`. Compare `data.length` with `requestedIds.length` to detect omissions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Document Statuses
|
## Document Statuses
|
||||||
@@ -740,6 +833,7 @@ const documents = await response.json();
|
|||||||
| `PENDING` | Document has been sent. Waiting for recipients to sign. |
|
| `PENDING` | Document has been sent. Waiting for recipients to sign. |
|
||||||
| `COMPLETED` | All recipients have signed. Document is sealed. |
|
| `COMPLETED` | All recipients have signed. Document is sealed. |
|
||||||
| `REJECTED` | A recipient rejected the document. |
|
| `REJECTED` | A recipient rejected the document. |
|
||||||
|
| `CANCELLED` | The document was cancelled by its owner or a team member with `MANAGER` or higher permissions. |
|
||||||
|
|
||||||
### Status Transitions
|
### Status Transitions
|
||||||
|
|
||||||
@@ -747,11 +841,13 @@ const documents = await response.json();
|
|||||||
flowchart LR
|
flowchart LR
|
||||||
DRAFT --> PENDING --> COMPLETED
|
DRAFT --> PENDING --> COMPLETED
|
||||||
PENDING --> REJECTED
|
PENDING --> REJECTED
|
||||||
|
PENDING --> CANCELLED
|
||||||
```
|
```
|
||||||
|
|
||||||
- **DRAFT to PENDING**: Call the distribute endpoint
|
- **DRAFT to PENDING**: Call the distribute endpoint
|
||||||
- **PENDING to COMPLETED**: All recipients complete their signing
|
- **PENDING to COMPLETED**: All recipients complete their signing
|
||||||
- **PENDING to REJECTED**: A recipient rejects the document
|
- **PENDING to REJECTED**: A recipient rejects the document
|
||||||
|
- **PENDING to CANCELLED**: The document owner or a team member with `MANAGER` or higher permissions cancels the document
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
You cannot modify recipients or fields after a document moves to `PENDING` status.
|
You cannot modify recipients or fields after a document moves to `PENDING` status.
|
||||||
@@ -773,8 +869,8 @@ flowchart LR
|
|||||||
| Parameter | Values | Description |
|
| Parameter | Values | Description |
|
||||||
| ---------- | ------------------------------------------- | ------------------------- |
|
| ---------- | ------------------------------------------- | ------------------------- |
|
||||||
| `type` | `DOCUMENT`, `TEMPLATE` | Filter by envelope type |
|
| `type` | `DOCUMENT`, `TEMPLATE` | Filter by envelope type |
|
||||||
| `status` | `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED` | Filter by status |
|
| `status` | `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED`, `CANCELLED` | Filter by status |
|
||||||
| `source` | `DOCUMENT`, `TEMPLATE`, `API` | Filter by creation source |
|
| `source` | `DOCUMENT`, `TEMPLATE`, `TEMPLATE_DIRECT_LINK` | Filter by creation source |
|
||||||
| `folderId` | string | Filter by folder |
|
| `folderId` | string | Filter by folder |
|
||||||
|
|
||||||
### Sorting
|
### Sorting
|
||||||
@@ -800,10 +896,10 @@ async function getAllPendingDocuments() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, pagination } = await response.json();
|
const { data, currentPage, totalPages } = await response.json();
|
||||||
documents.push(...data);
|
documents.push(...data);
|
||||||
|
|
||||||
hasMore = page < pagination.totalPages;
|
hasMore = currentPage < totalPages;
|
||||||
page++;
|
page++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ Full reference in the [V2 OpenAPI reference](https://openapi.documenso.com).
|
|||||||
| ------------------------------------------------- | ----------------------------------------------------- |
|
| ------------------------------------------------- | ----------------------------------------------------- |
|
||||||
| `GET /api/v2/document` | `GET /api/v2/envelope` |
|
| `GET /api/v2/document` | `GET /api/v2/envelope` |
|
||||||
| `GET /api/v2/document/{documentId}` | `GET /api/v2/envelope/{envelopeId}` |
|
| `GET /api/v2/document/{documentId}` | `GET /api/v2/envelope/{envelopeId}` |
|
||||||
| `POST /api/v2/document/get-many` | `POST /api/v2/envelope/get-many` |
|
| `POST /api/v2/document/get-many` | `POST /api/v2/envelope/get-many` (body changes from `documentIds: number[]` to `ids: { type: "documentId"; ids: number[] }`) |
|
||||||
| `POST /api/v2/document/create` | `POST /api/v2/envelope/create` |
|
| `POST /api/v2/document/create` | `POST /api/v2/envelope/create` |
|
||||||
| `POST /api/v2/document/create/beta` | `POST /api/v2/envelope/create` |
|
| `POST /api/v2/document/create/beta` | `POST /api/v2/envelope/create` |
|
||||||
| `POST /api/v2/document/update` | `POST /api/v2/envelope/update` |
|
| `POST /api/v2/document/update` | `POST /api/v2/envelope/update` |
|
||||||
@@ -140,7 +140,7 @@ Full reference in the [V2 OpenAPI reference](https://openapi.documenso.com).
|
|||||||
| ------------------------------------- | ------------------------------------------------ |
|
| ------------------------------------- | ------------------------------------------------ |
|
||||||
| `GET /api/v2/template` | `GET /api/v2/envelope` (with `type=TEMPLATE`) |
|
| `GET /api/v2/template` | `GET /api/v2/envelope` (with `type=TEMPLATE`) |
|
||||||
| `GET /api/v2/template/{templateId}` | `GET /api/v2/envelope/{envelopeId}` |
|
| `GET /api/v2/template/{templateId}` | `GET /api/v2/envelope/{envelopeId}` |
|
||||||
| `POST /api/v2/template/get-many` | `POST /api/v2/envelope/get-many` |
|
| `POST /api/v2/template/get-many` | `POST /api/v2/envelope/get-many` (body changes from `templateIds: number[]` to `ids: { type: "templateId"; ids: number[] }`) |
|
||||||
| `POST /api/v2/template/create` | `POST /api/v2/envelope/create` (`type=TEMPLATE`) |
|
| `POST /api/v2/template/create` | `POST /api/v2/envelope/create` (`type=TEMPLATE`) |
|
||||||
| `POST /api/v2/template/create/beta` | `POST /api/v2/envelope/create` (`type=TEMPLATE`) |
|
| `POST /api/v2/template/create/beta` | `POST /api/v2/envelope/create` (`type=TEMPLATE`) |
|
||||||
| `POST /api/v2/template/update` | `POST /api/v2/envelope/update` |
|
| `POST /api/v2/template/update` | `POST /api/v2/envelope/update` |
|
||||||
|
|||||||
Reference in New Issue
Block a user