@xenterprises/fastify-xsignwell 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +60 -0
- package/README.md +137 -302
- package/index.d.ts +164 -0
- package/package.json +2 -2
- package/src/apiRequest.js +49 -0
- package/src/services/bulkSend.js +67 -94
- package/src/services/documents.js +118 -105
- package/src/services/templates.js +64 -95
- package/src/services/webhooks.js +49 -83
- package/src/xSignwell.js +20 -27
- package/.gitlab-ci.yml +0 -45
- package/test/xSignwell.test.js +0 -539
package/LICENSE
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 X Enterprises LLC. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
exclusive property of X Enterprises LLC, a Washington limited liability
|
|
7
|
+
company.
|
|
8
|
+
|
|
9
|
+
TERMS AND CONDITIONS
|
|
10
|
+
|
|
11
|
+
1. OWNERSHIP
|
|
12
|
+
All rights, title, and interest in and to the Software, including all
|
|
13
|
+
intellectual property rights, are and shall remain the exclusive property
|
|
14
|
+
of X Enterprises LLC.
|
|
15
|
+
|
|
16
|
+
2. RESTRICTIONS
|
|
17
|
+
Without the prior written consent of X Enterprises LLC, you may not:
|
|
18
|
+
- Copy, modify, or distribute the Software
|
|
19
|
+
- Reverse engineer, decompile, or disassemble the Software
|
|
20
|
+
- Sublicense, sell, lease, or otherwise transfer the Software
|
|
21
|
+
- Remove or alter any proprietary notices or labels
|
|
22
|
+
|
|
23
|
+
3. AUTHORIZED USE
|
|
24
|
+
Use of this Software is limited to authorized employees, contractors, and
|
|
25
|
+
agents of X Enterprises LLC, solely for purposes approved by X Enterprises
|
|
26
|
+
LLC.
|
|
27
|
+
|
|
28
|
+
4. NO WARRANTY
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
32
|
+
X ENTERPRISES LLC BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
|
|
33
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF,
|
|
34
|
+
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
|
+
SOFTWARE.
|
|
36
|
+
|
|
37
|
+
5. LIMITATION OF LIABILITY
|
|
38
|
+
IN NO EVENT SHALL X ENTERPRISES LLC BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
|
|
39
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
40
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
41
|
+
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
42
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
43
|
+
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
44
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
45
|
+
|
|
46
|
+
6. GOVERNING LAW
|
|
47
|
+
This license shall be governed by and construed in accordance with the laws
|
|
48
|
+
of the State of Washington, United States, without regard to its conflict
|
|
49
|
+
of law provisions.
|
|
50
|
+
|
|
51
|
+
7. TERMINATION
|
|
52
|
+
This license is effective until terminated. X Enterprises LLC may terminate
|
|
53
|
+
this license at any time without notice. Upon termination, you must destroy
|
|
54
|
+
all copies of the Software in your possession.
|
|
55
|
+
|
|
56
|
+
For licensing inquiries, contact: legal@x.enterprises
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
X Enterprises LLC
|
|
60
|
+
Bothell, Washington, United States
|
package/README.md
CHANGED
|
@@ -1,333 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @xenterprises/fastify-xsignwell
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Fastify plugin for [SignWell](https://www.signwell.com/) e-signature API integration — document creation, templates, bulk sending, webhooks, and embedded signing.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- **Document Management**: Create, send, and track documents for signing
|
|
8
|
-
- **Template Support**: Create and manage reusable document templates
|
|
9
|
-
- **Embedded Signing**: Get URLs for embedded signing experiences
|
|
10
|
-
- **Bulk Send**: Send documents to multiple recipients at once
|
|
11
|
-
- **Webhooks**: Register and manage webhook callbacks for document events
|
|
12
|
-
- **Test Mode**: Built-in support for SignWell's test mode
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
5
|
+
## Install
|
|
15
6
|
|
|
16
7
|
```bash
|
|
17
|
-
npm install xsignwell
|
|
8
|
+
npm install @xenterprises/fastify-xsignwell
|
|
18
9
|
```
|
|
19
10
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
### Environment Variables
|
|
23
|
-
|
|
24
|
-
| Variable | Description | Required |
|
|
25
|
-
|----------|-------------|----------|
|
|
26
|
-
| `SIGNWELL_API_KEY` | SignWell API key | Yes |
|
|
27
|
-
| `SIGNWELL_TEST_MODE` | Enable test mode | No |
|
|
28
|
-
|
|
29
|
-
### Plugin Registration
|
|
11
|
+
## Quick Start
|
|
30
12
|
|
|
31
13
|
```javascript
|
|
32
|
-
import Fastify from
|
|
33
|
-
import xSignwell from
|
|
14
|
+
import Fastify from "fastify";
|
|
15
|
+
import xSignwell from "@xenterprises/fastify-xsignwell";
|
|
34
16
|
|
|
35
17
|
const fastify = Fastify();
|
|
36
18
|
|
|
37
19
|
await fastify.register(xSignwell, {
|
|
38
20
|
apiKey: process.env.SIGNWELL_API_KEY,
|
|
39
|
-
testMode: process.env.NODE_ENV !==
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
|
-
### Documents
|
|
46
|
-
|
|
47
|
-
#### Create a Document
|
|
48
|
-
|
|
49
|
-
```javascript
|
|
50
|
-
const document = await fastify.xsignwell.documents.create({
|
|
51
|
-
name: 'Employment Agreement',
|
|
52
|
-
recipients: [
|
|
53
|
-
{
|
|
54
|
-
id: 'signer_1',
|
|
55
|
-
name: 'John Doe',
|
|
56
|
-
email: 'john@example.com',
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
files: [
|
|
60
|
-
{
|
|
61
|
-
name: 'agreement.pdf',
|
|
62
|
-
url: 'https://example.com/agreement.pdf',
|
|
63
|
-
// OR use base64:
|
|
64
|
-
// base64: 'JVBERi0xLjQK...',
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
subject: 'Please sign your employment agreement',
|
|
68
|
-
message: 'Hi John, please review and sign the attached agreement.',
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
#### Create from Template
|
|
73
|
-
|
|
74
|
-
```javascript
|
|
75
|
-
const document = await fastify.xsignwell.documents.createFromTemplate(
|
|
76
|
-
'template_id_here',
|
|
77
|
-
{
|
|
78
|
-
recipients: [
|
|
79
|
-
{
|
|
80
|
-
id: 'signer_1', // Must match template recipient ID
|
|
81
|
-
name: 'John Doe',
|
|
82
|
-
email: 'john@example.com',
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
fields: {
|
|
86
|
-
employee_name: 'John Doe',
|
|
87
|
-
start_date: '2025-02-01',
|
|
88
|
-
salary: '$75,000',
|
|
89
|
-
},
|
|
90
|
-
}
|
|
91
|
-
);
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
#### Get Document Status
|
|
95
|
-
|
|
96
|
-
```javascript
|
|
97
|
-
const document = await fastify.xsignwell.documents.get('document_id');
|
|
98
|
-
console.log(document.status); // 'pending', 'completed', etc.
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
#### Send a Document
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
await fastify.xsignwell.documents.send('document_id');
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
#### Send Reminder
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
await fastify.xsignwell.documents.remind('document_id');
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
#### Get Completed PDF
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
const pdf = await fastify.xsignwell.documents.getCompletedPdf('document_id');
|
|
117
|
-
// pdf.file_url contains the download URL
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
#### Get Embedded Signing URL
|
|
121
|
-
|
|
122
|
-
```javascript
|
|
123
|
-
const { url, recipient } = await fastify.xsignwell.documents.getEmbeddedSigningUrl(
|
|
124
|
-
'document_id',
|
|
125
|
-
'recipient_id'
|
|
126
|
-
);
|
|
127
|
-
// Redirect user to `url` for signing
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### Delete Document
|
|
131
|
-
|
|
132
|
-
```javascript
|
|
133
|
-
await fastify.xsignwell.documents.remove('document_id');
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Templates
|
|
137
|
-
|
|
138
|
-
#### Get Template
|
|
139
|
-
|
|
140
|
-
```javascript
|
|
141
|
-
const template = await fastify.xsignwell.templates.get('template_id');
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
#### List Templates
|
|
145
|
-
|
|
146
|
-
```javascript
|
|
147
|
-
const templates = await fastify.xsignwell.templates.list({
|
|
148
|
-
page: 1,
|
|
149
|
-
limit: 20,
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
#### Create Template
|
|
154
|
-
|
|
155
|
-
```javascript
|
|
156
|
-
const template = await fastify.xsignwell.templates.create({
|
|
157
|
-
name: 'NDA Template',
|
|
158
|
-
files: [
|
|
159
|
-
{
|
|
160
|
-
name: 'nda.pdf',
|
|
161
|
-
url: 'https://example.com/nda.pdf',
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
recipients: [
|
|
165
|
-
{ id: 'signer_1', name: 'Signer' },
|
|
166
|
-
{ id: 'signer_2', name: 'Counter-Signer' },
|
|
167
|
-
],
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### Update Template
|
|
172
|
-
|
|
173
|
-
```javascript
|
|
174
|
-
await fastify.xsignwell.templates.update('template_id', {
|
|
175
|
-
name: 'Updated NDA Template',
|
|
176
|
-
subject: 'New subject line',
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
#### Delete Template
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
await fastify.xsignwell.templates.remove('template_id');
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
#### Get Template Fields
|
|
187
|
-
|
|
188
|
-
```javascript
|
|
189
|
-
const fields = await fastify.xsignwell.templates.getFields('template_id');
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Bulk Send
|
|
193
|
-
|
|
194
|
-
#### Create Bulk Send
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
const bulkSend = await fastify.xsignwell.bulkSend.create({
|
|
198
|
-
templateId: 'template_id',
|
|
199
|
-
recipients: [
|
|
200
|
-
{
|
|
201
|
-
email: 'john@example.com',
|
|
202
|
-
name: 'John Doe',
|
|
203
|
-
// Field values for this recipient
|
|
204
|
-
employee_name: 'John Doe',
|
|
205
|
-
start_date: '2025-02-01',
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
email: 'jane@example.com',
|
|
209
|
-
name: 'Jane Smith',
|
|
210
|
-
employee_name: 'Jane Smith',
|
|
211
|
-
start_date: '2025-02-15',
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
});
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
#### List Bulk Sends
|
|
218
|
-
|
|
219
|
-
```javascript
|
|
220
|
-
const bulkSends = await fastify.xsignwell.bulkSend.list();
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
#### Get Bulk Send Documents
|
|
224
|
-
|
|
225
|
-
```javascript
|
|
226
|
-
const documents = await fastify.xsignwell.bulkSend.getDocuments('bulk_send_id');
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
#### Get CSV Template
|
|
230
|
-
|
|
231
|
-
```javascript
|
|
232
|
-
const csvTemplate = await fastify.xsignwell.bulkSend.getCsvTemplate('template_id');
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Webhooks
|
|
236
|
-
|
|
237
|
-
#### List Webhooks
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
const webhooks = await fastify.xsignwell.webhooks.list();
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
#### Create Webhook
|
|
244
|
-
|
|
245
|
-
```javascript
|
|
246
|
-
const webhook = await fastify.xsignwell.webhooks.create({
|
|
247
|
-
callbackUrl: 'https://your-api.com/webhooks/signwell',
|
|
248
|
-
event: 'document.completed', // Optional: subscribe to specific event
|
|
21
|
+
testMode: process.env.NODE_ENV !== "production",
|
|
249
22
|
});
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
#### Delete Webhook
|
|
253
23
|
|
|
254
|
-
|
|
255
|
-
await fastify.xsignwell.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
```javascript
|
|
261
|
-
fastify.post('/webhooks/signwell', async (request, reply) => {
|
|
262
|
-
const event = fastify.xsignwell.webhooks.parseEvent(request.body);
|
|
263
|
-
|
|
264
|
-
switch (event.event) {
|
|
265
|
-
case 'document.completed':
|
|
266
|
-
console.log(`Document ${event.documentId} completed`);
|
|
267
|
-
// Download the completed PDF
|
|
268
|
-
const pdf = await fastify.xsignwell.documents.getCompletedPdf(event.documentId);
|
|
269
|
-
break;
|
|
270
|
-
|
|
271
|
-
case 'document.signed':
|
|
272
|
-
console.log(`Document ${event.documentId} signed by ${event.recipient?.email}`);
|
|
273
|
-
break;
|
|
274
|
-
|
|
275
|
-
case 'document.declined':
|
|
276
|
-
console.log(`Document ${event.documentId} was declined`);
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return { received: true };
|
|
24
|
+
// Create and send a document
|
|
25
|
+
const doc = await fastify.xsignwell.documents.create({
|
|
26
|
+
name: "Employment Agreement",
|
|
27
|
+
recipients: [{ email: "john@example.com", name: "John Doe" }],
|
|
28
|
+
files: [{ name: "agreement.pdf", url: "https://example.com/agreement.pdf" }],
|
|
281
29
|
});
|
|
282
30
|
```
|
|
283
31
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
```javascript
|
|
287
|
-
const events = fastify.xsignwell.webhooks.events;
|
|
288
|
-
// {
|
|
289
|
-
// DOCUMENT_COMPLETED: 'document.completed',
|
|
290
|
-
// DOCUMENT_SENT: 'document.sent',
|
|
291
|
-
// DOCUMENT_VIEWED: 'document.viewed',
|
|
292
|
-
// DOCUMENT_SIGNED: 'document.signed',
|
|
293
|
-
// DOCUMENT_DECLINED: 'document.declined',
|
|
294
|
-
// DOCUMENT_EXPIRED: 'document.expired',
|
|
295
|
-
// DOCUMENT_VOIDED: 'document.voided',
|
|
296
|
-
// RECIPIENT_COMPLETED: 'recipient.completed',
|
|
297
|
-
// RECIPIENT_VIEWED: 'recipient.viewed',
|
|
298
|
-
// RECIPIENT_SIGNED: 'recipient.signed',
|
|
299
|
-
// RECIPIENT_DECLINED: 'recipient.declined',
|
|
300
|
-
// }
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Account Info
|
|
32
|
+
## Options
|
|
304
33
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
34
|
+
| Name | Type | Default | Required | Description |
|
|
35
|
+
|------|------|---------|----------|-------------|
|
|
36
|
+
| `apiKey` | `string` | — | Yes | SignWell API key |
|
|
37
|
+
| `baseUrl` | `string` | `https://www.signwell.com/api/v1` | No | API base URL |
|
|
38
|
+
| `testMode` | `boolean` | `false` | No | Enable test mode (documents won't actually send) |
|
|
39
|
+
| `active` | `boolean` | `true` | No | Set `false` to disable the plugin entirely |
|
|
309
40
|
|
|
310
|
-
##
|
|
41
|
+
## Environment Variables
|
|
311
42
|
|
|
312
|
-
|
|
43
|
+
| Name | Required | Description |
|
|
44
|
+
|------|----------|-------------|
|
|
45
|
+
| `SIGNWELL_API_KEY` | Yes | SignWell API key |
|
|
46
|
+
| `SIGNWELL_TEST_MODE` | No | Set `"true"` to enable test mode |
|
|
313
47
|
|
|
314
|
-
|
|
48
|
+
## Decorated Properties
|
|
315
49
|
|
|
316
50
|
| Decorator | Description |
|
|
317
51
|
|-----------|-------------|
|
|
52
|
+
| `fastify.xsignwell.config` | Plugin configuration object |
|
|
318
53
|
| `fastify.xsignwell.documents` | Document management methods |
|
|
319
54
|
| `fastify.xsignwell.templates` | Template management methods |
|
|
320
55
|
| `fastify.xsignwell.bulkSend` | Bulk send methods |
|
|
321
56
|
| `fastify.xsignwell.webhooks` | Webhook management methods |
|
|
322
|
-
| `fastify.xsignwell.me` | Get account info |
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
57
|
+
| `fastify.xsignwell.me()` | Get current account info |
|
|
58
|
+
|
|
59
|
+
### `documents`
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|--------|-------------|
|
|
63
|
+
| `create(params)` | Create a new document for signing |
|
|
64
|
+
| `createFromTemplate(templateId, params)` | Create a document from a template |
|
|
65
|
+
| `get(documentId)` | Get document details by ID |
|
|
66
|
+
| `list(params?)` | List documents (page, limit) |
|
|
67
|
+
| `send(documentId, params?)` | Send a draft document |
|
|
68
|
+
| `remind(documentId)` | Send reminder to unsigned recipients |
|
|
69
|
+
| `remove(documentId)` | Delete a document |
|
|
70
|
+
| `getCompletedPdf(documentId)` | Get download URL for completed PDF |
|
|
71
|
+
| `getEmbeddedSigningUrl(documentId, recipientId)` | Get embedded signing URL |
|
|
72
|
+
| `getAuditTrail(documentId)` | Get document audit trail |
|
|
73
|
+
|
|
74
|
+
### `templates`
|
|
75
|
+
|
|
76
|
+
| Method | Description |
|
|
77
|
+
|--------|-------------|
|
|
78
|
+
| `get(templateId)` | Get template by ID |
|
|
79
|
+
| `create(params)` | Create a new template |
|
|
80
|
+
| `update(templateId, params)` | Update an existing template |
|
|
81
|
+
| `remove(templateId)` | Delete a template |
|
|
82
|
+
| `list(params?)` | List all templates (page, limit) |
|
|
83
|
+
| `getFields(templateId)` | Get template field definitions |
|
|
84
|
+
| `getRecipients(templateId)` | Get template recipient placeholders |
|
|
85
|
+
|
|
86
|
+
### `bulkSend`
|
|
87
|
+
|
|
88
|
+
| Method | Description |
|
|
89
|
+
|--------|-------------|
|
|
90
|
+
| `get(bulkSendId)` | Get bulk send by ID |
|
|
91
|
+
| `list(params?)` | List all bulk sends (page, limit) |
|
|
92
|
+
| `create(params)` | Create a bulk send from a template |
|
|
93
|
+
| `getCsvTemplate(templateId)` | Get CSV template for bulk upload |
|
|
94
|
+
| `validateCsv(templateId, csvContent)` | Validate CSV before sending |
|
|
95
|
+
| `getDocuments(bulkSendId, params?)` | Get documents from a bulk send |
|
|
96
|
+
|
|
97
|
+
### `webhooks`
|
|
98
|
+
|
|
99
|
+
| Method | Description |
|
|
100
|
+
|--------|-------------|
|
|
101
|
+
| `list()` | List all webhooks |
|
|
102
|
+
| `create(params)` | Create a new webhook |
|
|
103
|
+
| `remove(webhookId)` | Delete a webhook |
|
|
104
|
+
| `events` | Event type constants (see below) |
|
|
105
|
+
| `verifySignature(payload, signature, secret)` | Verify webhook HMAC-SHA256 signature |
|
|
106
|
+
| `parseEvent(payload)` | Parse webhook payload into structured event |
|
|
107
|
+
|
|
108
|
+
#### Webhook Event Constants
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_COMPLETED // "document.completed"
|
|
112
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_SENT // "document.sent"
|
|
113
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_VIEWED // "document.viewed"
|
|
114
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_SIGNED // "document.signed"
|
|
115
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_DECLINED // "document.declined"
|
|
116
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_EXPIRED // "document.expired"
|
|
117
|
+
fastify.xsignwell.webhooks.events.DOCUMENT_VOIDED // "document.voided"
|
|
118
|
+
fastify.xsignwell.webhooks.events.RECIPIENT_COMPLETED // "recipient.completed"
|
|
119
|
+
fastify.xsignwell.webhooks.events.RECIPIENT_VIEWED // "recipient.viewed"
|
|
120
|
+
fastify.xsignwell.webhooks.events.RECIPIENT_SIGNED // "recipient.signed"
|
|
121
|
+
fastify.xsignwell.webhooks.events.RECIPIENT_DECLINED // "recipient.declined"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Error Reference
|
|
125
|
+
|
|
126
|
+
All errors thrown by the plugin are prefixed with `[xSignwell]`.
|
|
127
|
+
|
|
128
|
+
| Error | When |
|
|
129
|
+
|-------|------|
|
|
130
|
+
| `[xSignwell] apiKey is required` | Plugin registered without `apiKey` option |
|
|
131
|
+
| `[xSignwell] apiKey must be a string` | `apiKey` is not a string |
|
|
132
|
+
| `[xSignwell] baseUrl must be a string` | `baseUrl` is not a string |
|
|
133
|
+
| `[xSignwell] testMode must be a boolean` | `testMode` is not a boolean |
|
|
134
|
+
| `[xSignwell] documents.create: name (string) is required` | `create()` called without `name` |
|
|
135
|
+
| `[xSignwell] documents.create: recipients (non-empty array) is required` | `create()` missing recipients |
|
|
136
|
+
| `[xSignwell] documents.create: each recipient must have an email` | Recipient without email field |
|
|
137
|
+
| `[xSignwell] <service>.<method>: <param> is required` | Any method called with missing required param |
|
|
138
|
+
| `[xSignwell] Recipient <id> not found in document <id>` | `getEmbeddedSigningUrl` can't find recipient |
|
|
139
|
+
| `[xSignwell] API error: <status>` | SignWell API returned a non-2xx response |
|
|
140
|
+
| `[xSignwell] Network error calling <endpoint>: <message>` | Network/connection failure |
|
|
141
|
+
|
|
142
|
+
API errors include `statusCode` and `signwellError` properties on the Error object for programmatic handling:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
try {
|
|
146
|
+
await fastify.xsignwell.documents.get("bad_id");
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.log(err.statusCode); // 404
|
|
149
|
+
console.log(err.signwellError); // { message: "Document not found" }
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## How It Works
|
|
154
|
+
|
|
155
|
+
The plugin registers a single `xsignwell` decorator on the Fastify instance. On startup it validates options (apiKey type, baseUrl type, testMode type) and builds a config object. Four service modules (documents, templates, webhooks, bulkSend) attach their methods to sub-objects on the decorator.
|
|
156
|
+
|
|
157
|
+
All API calls go through a shared `apiRequest` helper that:
|
|
158
|
+
1. Adds the `X-Api-Key` header from config
|
|
159
|
+
2. Parses JSON responses (handles empty bodies for DELETE)
|
|
160
|
+
3. On non-2xx responses, attaches `statusCode` and `signwellError` to the thrown Error
|
|
161
|
+
4. On network failures, wraps with `[xSignwell] Network error` message
|
|
162
|
+
|
|
163
|
+
The plugin uses `fastify-plugin` with `fastify >=5.0.0` constraint so the decorator is visible across all scopes. The `testMode` config flag is passed as `test_mode` in document/bulk-send creation payloads so SignWell won't actually deliver signing emails.
|
|
331
164
|
|
|
332
165
|
## Testing
|
|
333
166
|
|
|
@@ -335,6 +168,8 @@ SignWell API has the following rate limits:
|
|
|
335
168
|
npm test
|
|
336
169
|
```
|
|
337
170
|
|
|
171
|
+
82 tests covering plugin registration, startup validation, all document/template/webhook/bulk-send methods (happy path + input validation + error propagation), webhook signature verification, and network error handling.
|
|
172
|
+
|
|
338
173
|
## License
|
|
339
174
|
|
|
340
|
-
|
|
175
|
+
UNLICENSED
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { FastifyPluginAsync } from "fastify";
|
|
2
|
+
|
|
3
|
+
interface XSignwellOptions {
|
|
4
|
+
/** SignWell API key (required) */
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/** API base URL (default: "https://www.signwell.com/api/v1") */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** Enable test mode (default: false) */
|
|
9
|
+
testMode?: boolean;
|
|
10
|
+
/** Enable/disable the plugin (default: true) */
|
|
11
|
+
active?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Recipient {
|
|
15
|
+
id?: string;
|
|
16
|
+
email: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
sendEmail?: boolean;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface FileAttachment {
|
|
23
|
+
name: string;
|
|
24
|
+
base64?: string;
|
|
25
|
+
file_base64?: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
file_url?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface CreateDocumentParams {
|
|
31
|
+
name: string;
|
|
32
|
+
recipients: Recipient[];
|
|
33
|
+
files?: FileAttachment[];
|
|
34
|
+
draft?: boolean;
|
|
35
|
+
testMode?: boolean;
|
|
36
|
+
subject?: string;
|
|
37
|
+
message?: string;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface CreateFromTemplateParams {
|
|
43
|
+
recipients: Recipient[];
|
|
44
|
+
fields?: Record<string, unknown>;
|
|
45
|
+
draft?: boolean;
|
|
46
|
+
testMode?: boolean;
|
|
47
|
+
subject?: string;
|
|
48
|
+
message?: string;
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface PaginationParams {
|
|
54
|
+
page?: number;
|
|
55
|
+
limit?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface DocumentsService {
|
|
59
|
+
create(params: CreateDocumentParams): Promise<Record<string, unknown>>;
|
|
60
|
+
createFromTemplate(templateId: string, params: CreateFromTemplateParams): Promise<Record<string, unknown>>;
|
|
61
|
+
get(documentId: string): Promise<Record<string, unknown>>;
|
|
62
|
+
list(params?: PaginationParams): Promise<Record<string, unknown>>;
|
|
63
|
+
send(documentId: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
64
|
+
remind(documentId: string): Promise<Record<string, unknown>>;
|
|
65
|
+
remove(documentId: string): Promise<Record<string, unknown>>;
|
|
66
|
+
delete(documentId: string): Promise<Record<string, unknown>>;
|
|
67
|
+
getCompletedPdf(documentId: string): Promise<Record<string, unknown>>;
|
|
68
|
+
getEmbeddedSigningUrl(documentId: string, recipientId: string): Promise<{ url: string; recipient: Record<string, unknown> }>;
|
|
69
|
+
getAuditTrail(documentId: string): Promise<Record<string, unknown>>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface CreateTemplateParams {
|
|
73
|
+
name: string;
|
|
74
|
+
files?: FileAttachment[];
|
|
75
|
+
recipients?: Array<{ id?: string; name?: string; placeholder_name?: string; [key: string]: unknown }>;
|
|
76
|
+
fields?: unknown[];
|
|
77
|
+
subject?: string;
|
|
78
|
+
message?: string;
|
|
79
|
+
[key: string]: unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface TemplatesService {
|
|
83
|
+
get(templateId: string): Promise<Record<string, unknown>>;
|
|
84
|
+
create(params: CreateTemplateParams): Promise<Record<string, unknown>>;
|
|
85
|
+
update(templateId: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
86
|
+
remove(templateId: string): Promise<Record<string, unknown>>;
|
|
87
|
+
delete(templateId: string): Promise<Record<string, unknown>>;
|
|
88
|
+
list(params?: PaginationParams): Promise<Record<string, unknown>>;
|
|
89
|
+
getFields(templateId: string): Promise<unknown[]>;
|
|
90
|
+
getRecipients(templateId: string): Promise<unknown[]>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface WebhookEvents {
|
|
94
|
+
DOCUMENT_COMPLETED: "document.completed";
|
|
95
|
+
DOCUMENT_SENT: "document.sent";
|
|
96
|
+
DOCUMENT_VIEWED: "document.viewed";
|
|
97
|
+
DOCUMENT_SIGNED: "document.signed";
|
|
98
|
+
DOCUMENT_DECLINED: "document.declined";
|
|
99
|
+
DOCUMENT_EXPIRED: "document.expired";
|
|
100
|
+
DOCUMENT_VOIDED: "document.voided";
|
|
101
|
+
RECIPIENT_COMPLETED: "recipient.completed";
|
|
102
|
+
RECIPIENT_VIEWED: "recipient.viewed";
|
|
103
|
+
RECIPIENT_SIGNED: "recipient.signed";
|
|
104
|
+
RECIPIENT_DECLINED: "recipient.declined";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface ParsedEvent {
|
|
108
|
+
event: string;
|
|
109
|
+
documentId: string;
|
|
110
|
+
document: Record<string, unknown>;
|
|
111
|
+
recipient: Record<string, unknown> | undefined;
|
|
112
|
+
timestamp: string;
|
|
113
|
+
raw: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface WebhooksService {
|
|
117
|
+
list(): Promise<Record<string, unknown>>;
|
|
118
|
+
create(params: { callbackUrl: string; event?: string; [key: string]: unknown }): Promise<Record<string, unknown>>;
|
|
119
|
+
remove(webhookId: string): Promise<Record<string, unknown>>;
|
|
120
|
+
delete(webhookId: string): Promise<Record<string, unknown>>;
|
|
121
|
+
events: WebhookEvents;
|
|
122
|
+
verifySignature(payload: string, signature: string, secret: string): boolean;
|
|
123
|
+
parseEvent(payload: Record<string, unknown>): ParsedEvent;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface CreateBulkSendParams {
|
|
127
|
+
templateId: string;
|
|
128
|
+
recipients: Record<string, unknown>[];
|
|
129
|
+
testMode?: boolean;
|
|
130
|
+
subject?: string;
|
|
131
|
+
message?: string;
|
|
132
|
+
[key: string]: unknown;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface BulkSendService {
|
|
136
|
+
get(bulkSendId: string): Promise<Record<string, unknown>>;
|
|
137
|
+
list(params?: PaginationParams): Promise<Record<string, unknown>>;
|
|
138
|
+
create(params: CreateBulkSendParams): Promise<Record<string, unknown>>;
|
|
139
|
+
getCsvTemplate(templateId: string): Promise<Record<string, unknown>>;
|
|
140
|
+
validateCsv(templateId: string, csvContent: string): Promise<Record<string, unknown>>;
|
|
141
|
+
getDocuments(bulkSendId: string, params?: PaginationParams): Promise<Record<string, unknown>>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface XSignwellDecorator {
|
|
145
|
+
config: {
|
|
146
|
+
apiKey: string;
|
|
147
|
+
baseUrl: string;
|
|
148
|
+
testMode: boolean;
|
|
149
|
+
};
|
|
150
|
+
documents: DocumentsService;
|
|
151
|
+
templates: TemplatesService;
|
|
152
|
+
webhooks: WebhooksService;
|
|
153
|
+
bulkSend: BulkSendService;
|
|
154
|
+
me(): Promise<Record<string, unknown>>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
declare module "fastify" {
|
|
158
|
+
interface FastifyInstance {
|
|
159
|
+
xsignwell: XSignwellDecorator;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
declare const xSignwell: FastifyPluginAsync<XSignwellOptions>;
|
|
164
|
+
export default xSignwell;
|