@assinafy/sdk 1.4.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 +19 -0
- package/README.md +466 -0
- package/dist/index.d.mts +1123 -0
- package/dist/index.d.ts +1123 -0
- package/dist/index.js +1564 -0
- package/dist/index.mjs +1511 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 Assinafy
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# @assinafy/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the [Assinafy API](https://api.assinafy.com.br/v1/docs) — a Brazilian digital signature platform.
|
|
4
|
+
|
|
5
|
+
Provides 100% endpoint coverage of the public API: documents, signers, assignments, templates, tags, workspaces, webhooks, field definitions, authentication, public/signer-side flows, and the high-level `uploadAndRequestSignatures` helper.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 18+ for the built-in `FormData` / `Blob` APIs used by uploads
|
|
10
|
+
- or Bun 1.0+
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @assinafy/sdk
|
|
16
|
+
# or
|
|
17
|
+
bun add @assinafy/sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The package is published to both [npmjs.com](https://www.npmjs.com/package/@assinafy/sdk) and [GitHub Packages](https://github.com/assinafy/typescript-sdk/packages). To install from GitHub Packages, add to your `.npmrc`:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
@assinafy:registry=https://npm.pkg.github.com
|
|
24
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { AssinafyClient } from '@assinafy/sdk';
|
|
31
|
+
|
|
32
|
+
const client = new AssinafyClient({
|
|
33
|
+
apiKey: process.env.ASSINAFY_API_KEY!,
|
|
34
|
+
accountId: process.env.ASSINAFY_ACCOUNT_ID!,
|
|
35
|
+
webhookSecret: process.env.ASSINAFY_WEBHOOK_SECRET,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const result = await client.uploadAndRequestSignatures({
|
|
39
|
+
source: { filePath: './contract.pdf' },
|
|
40
|
+
signers: [
|
|
41
|
+
{ name: 'John Doe', email: 'john@example.com' },
|
|
42
|
+
{ name: 'Jane Smith', email: 'jane@example.com', whatsapp_phone_number: '+5548999990000' },
|
|
43
|
+
],
|
|
44
|
+
message: 'Please sign this contract',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log('Document ID:', result.document.id);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Authentication
|
|
51
|
+
|
|
52
|
+
The API supports two authentication methods. Prefer `apiKey` — it maps to the `X-Api-Key` header recommended by Assinafy for backend services.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// Preferred: X-Api-Key header
|
|
56
|
+
new AssinafyClient({ apiKey: 'k_xxx', accountId: 'acc_xxx' });
|
|
57
|
+
|
|
58
|
+
// Legacy: Authorization: Bearer <token>
|
|
59
|
+
new AssinafyClient({ token: 'jwt_xxx', accountId: 'acc_xxx' });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
| Option | Type | Default | Description |
|
|
65
|
+
| --------------- | -------- | --------------------------------------- | --------------------------------------------- |
|
|
66
|
+
| `apiKey` | string | — | Preferred credential (sent as `X-Api-Key`). |
|
|
67
|
+
| `token` | string | — | Legacy access token (sent as `Bearer`). |
|
|
68
|
+
| `accountId` | string | — | Default workspace/account ID. |
|
|
69
|
+
| `baseUrl` | string | `https://api.assinafy.com.br/v1` | Override base URL. |
|
|
70
|
+
| `webhookSecret` | string | — | Shared secret used by `WebhookVerifier`. |
|
|
71
|
+
| `timeout` | number | `30000` | Request timeout in milliseconds. |
|
|
72
|
+
| `logger` | `Logger` | no-op | Optional `{debug,info,warn,error}` logger. |
|
|
73
|
+
|
|
74
|
+
### Factories
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// Positional factory
|
|
78
|
+
const client = AssinafyClient.create('api-key', 'account-id', { webhookSecret: 'shhh' });
|
|
79
|
+
|
|
80
|
+
// From a plain object (accepts snake_case or camelCase keys)
|
|
81
|
+
const client = AssinafyClient.fromConfig({
|
|
82
|
+
api_key: process.env.ASSINAFY_API_KEY!,
|
|
83
|
+
account_id: process.env.ASSINAFY_ACCOUNT_ID!,
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Endpoint coverage
|
|
88
|
+
|
|
89
|
+
Every public endpoint documented in https://api.assinafy.com.br/v1/docs is covered. The table below maps each resource to its API surface.
|
|
90
|
+
|
|
91
|
+
| Resource | Endpoints |
|
|
92
|
+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `client.documents` | list, upload, details, activities, waitUntilReady, download, thumbnail, downloadPage, statuses, delete, verify, createFromTemplate, estimateCostFromTemplate, **getPublic**, **sendToken**, **listTags**, **replaceTags**, **addTags**, **detachTag**, isFullySigned, getSigningProgress |
|
|
94
|
+
| `client.signers` | create, get, list, update, delete, findByEmail |
|
|
95
|
+
| `client.assignments` | create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications, cancel |
|
|
96
|
+
| `client.templates` | list, get, downloadPage |
|
|
97
|
+
| `client.tags` | list, create, update, delete |
|
|
98
|
+
| `client.workspaces` | create, list, get, update, delete |
|
|
99
|
+
| `client.webhooks` | register, get, inactivate, delete, listEventTypes, listDispatches, retryDispatch |
|
|
100
|
+
| `client.fields` | create, list, get, update, delete, validate, validateMultiple, listTypes |
|
|
101
|
+
| `client.auth` | login, socialLogin, createApiKey, getApiKey, deleteApiKey, changePassword, requestPasswordReset, resetPassword |
|
|
102
|
+
| `client.signerDocuments` | getCurrent, list, download, signMultiple, declineMultiple, self, acceptTerms, verifyEmail, confirmData, uploadSignature, downloadSignature, getAssignment, sign, decline |
|
|
103
|
+
| `client.webhookVerifier` | verify, extractEvent, getEventType, getEventData |
|
|
104
|
+
|
|
105
|
+
## Resources
|
|
106
|
+
|
|
107
|
+
Most account-scoped methods accept an optional `accountId` that overrides the client default. Workspace `get/update/delete` always require an explicit account ID.
|
|
108
|
+
|
|
109
|
+
### Documents
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// Upload from a file path (recommended)
|
|
113
|
+
const doc = await client.documents.upload(
|
|
114
|
+
{ filePath: './contract.pdf' },
|
|
115
|
+
{ metadata: { type: 'service' } },
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// …or from a Buffer already in memory
|
|
119
|
+
await client.documents.upload({ buffer, fileName: 'contract.pdf' });
|
|
120
|
+
|
|
121
|
+
const { data, meta } = await client.documents.list({ page: 1, per_page: 20, sort: '-created_at' });
|
|
122
|
+
await client.documents.details(doc.id);
|
|
123
|
+
await client.documents.activities(doc.id);
|
|
124
|
+
await client.documents.waitUntilReady(doc.id, { maxWaitMs: 30_000 });
|
|
125
|
+
|
|
126
|
+
await client.documents.download(doc.id, 'certificated'); // 'original' | 'certificated' | 'certificate-page' | 'bundle'
|
|
127
|
+
await client.documents.thumbnail(doc.id);
|
|
128
|
+
await client.documents.downloadPage(doc.id, pageId);
|
|
129
|
+
|
|
130
|
+
await client.documents.statuses(); // list every status code + deletable flag
|
|
131
|
+
await client.documents.isFullySigned(doc.id);
|
|
132
|
+
await client.documents.getSigningProgress(doc.id);
|
|
133
|
+
await client.documents.delete(doc.id);
|
|
134
|
+
|
|
135
|
+
// Verify a signed document by its SHA-1 hash
|
|
136
|
+
await client.documents.verify('FE32EDDADE7CBDDCBB934E7402047450B0E59C02');
|
|
137
|
+
|
|
138
|
+
// Public endpoints (no auth)
|
|
139
|
+
await client.documents.getPublic(doc.id);
|
|
140
|
+
await client.documents.sendToken(doc.id, 'jane@example.com', 'email');
|
|
141
|
+
|
|
142
|
+
// Tags attached to a document (by tag name; unknown names are auto-created)
|
|
143
|
+
await client.documents.listTags(doc.id);
|
|
144
|
+
await client.documents.replaceTags(doc.id, ['Contracts', '2026-Q1']); // [] detaches all
|
|
145
|
+
await client.documents.addTags(doc.id, ['Urgent']); // append, idempotent
|
|
146
|
+
await client.documents.detachTag(doc.id, tagId); // remove one
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Uploads are validated locally: only `.pdf` files up to 25 MB are accepted (the API's current hard limit).
|
|
150
|
+
|
|
151
|
+
List endpoints return `{ data, meta }` where `meta` is populated from the `X-Pagination-*` headers returned by the API.
|
|
152
|
+
|
|
153
|
+
### Signers
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
await client.signers.create({
|
|
157
|
+
full_name: 'John Doe',
|
|
158
|
+
email: 'john@example.com',
|
|
159
|
+
whatsapp_phone_number: '+5548999990000',
|
|
160
|
+
cpf: '123.456.789-00', // optional Brazilian tax ID — non-digits are stripped automatically
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// `email` is optional — a WhatsApp-only signer is valid (at least one is required)
|
|
164
|
+
await client.signers.create({
|
|
165
|
+
full_name: 'WhatsApp Only',
|
|
166
|
+
whatsapp_phone_number: '+5548999990000',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// PHP SDK compatibility aliases are also accepted
|
|
170
|
+
await client.signers.create({
|
|
171
|
+
full_name: 'Jane Doe',
|
|
172
|
+
email: 'jane@example.com',
|
|
173
|
+
phone: '+5548999991111', // alias for whatsapp_phone_number
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await client.signers.get(signerId);
|
|
177
|
+
await client.signers.list({ page: 1, per_page: 50, search: 'john' });
|
|
178
|
+
await client.signers.update(signerId, { full_name: 'Johnny Doe' });
|
|
179
|
+
await client.signers.delete(signerId);
|
|
180
|
+
|
|
181
|
+
const existing = await client.signers.findByEmail('john@example.com');
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
When an `email` is supplied, `signers.create()` is idempotent by email, matching the PHP SDK behavior: it reuses an existing signer when the same email is already present in the workspace. WhatsApp-only signers (no email) are always created fresh.
|
|
185
|
+
|
|
186
|
+
### Assignments
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
// Signers may be ids or objects — the SDK normalises to the API shape.
|
|
190
|
+
await client.assignments.create(documentId, {
|
|
191
|
+
method: 'virtual',
|
|
192
|
+
signers: ['signer-1', 'signer-2'],
|
|
193
|
+
message: 'Please review and sign',
|
|
194
|
+
expires_at: '2024-12-31T23:59:00Z',
|
|
195
|
+
copy_receivers: ['observer-id'],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Sequential signing: `step` controls signing order (parallel within a step).
|
|
199
|
+
await client.assignments.create(documentId, {
|
|
200
|
+
method: 'virtual',
|
|
201
|
+
signers: [
|
|
202
|
+
{ id: 'signer-1', step: 1 },
|
|
203
|
+
{ id: 'signer-2', step: 2 }, // notified only after step 1 finishes
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Estimate cost (signers may omit `id` when only the channel matters)
|
|
208
|
+
await client.assignments.estimateCost(documentId, { signers: ['signer-1'] });
|
|
209
|
+
await client.assignments.estimateCost(documentId, {
|
|
210
|
+
signers: [{ verification_method: 'Whatsapp' }],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await client.assignments.resetExpiration(documentId, assignmentId, '2025-06-30T00:00:00Z');
|
|
214
|
+
await client.assignments.resetExpiration(documentId, assignmentId, null); // remove expiration
|
|
215
|
+
await client.assignments.resendNotification(documentId, assignmentId, signerId);
|
|
216
|
+
await client.assignments.estimateResendCost(documentId, assignmentId, signerId);
|
|
217
|
+
await client.assignments.listWhatsAppNotifications(documentId, assignmentId);
|
|
218
|
+
await client.assignments.cancel(documentId, 'No longer needed');
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
For backwards compatibility, the SDK also accepts legacy `signer_ids` and `signerIds` payloads and rewrites them to the current `signers: [{ id }]` format expected by the API.
|
|
222
|
+
|
|
223
|
+
### Templates
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const { data, meta } = await client.templates.list({ search: 'NDA', per_page: 20 });
|
|
227
|
+
const template = await client.templates.get(templateId);
|
|
228
|
+
await client.templates.downloadPage(templateId, pageId);
|
|
229
|
+
|
|
230
|
+
// Create a document from a template (each signer maps to a template role)
|
|
231
|
+
await client.documents.createFromTemplate(
|
|
232
|
+
templateId,
|
|
233
|
+
[{ role_id: template.roles![0].id, id: signerId, verification_method: 'Email', notification_methods: ['Email'] }],
|
|
234
|
+
{ name: 'NDA - John Doe', message: 'Please sign at your earliest convenience.' },
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Estimate the cost before creating
|
|
238
|
+
await client.documents.estimateCostFromTemplate(templateId, [{ role_id: 'role_id', id: signerId }]);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Tags
|
|
242
|
+
|
|
243
|
+
Workspace-scoped labels that can be attached to documents and templates. Tag names are unique per workspace (case-insensitive).
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
await client.tags.list({ search: 'contract' }); // ITag[]
|
|
247
|
+
const tag = await client.tags.create({ name: 'Contracts', color: 'ff8800' });
|
|
248
|
+
await client.tags.update(tag.id, { name: 'Sales Contracts' });
|
|
249
|
+
await client.tags.update(tag.id, { color: null }); // clear the color
|
|
250
|
+
await client.tags.delete(tag.id); // 409 if still attached
|
|
251
|
+
await client.tags.delete(tag.id, { force: true }); // detach everywhere, then delete
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Attach/detach tags on a specific document via `client.documents.listTags / replaceTags / addTags / detachTag` (see [Documents](#documents)).
|
|
255
|
+
|
|
256
|
+
### Workspaces
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
await client.workspaces.create({ name: 'My Workspace', primary_color: '#ff0066' });
|
|
260
|
+
await client.workspaces.list();
|
|
261
|
+
await client.workspaces.get(accountId);
|
|
262
|
+
await client.workspaces.update(accountId, { name: 'Renamed' });
|
|
263
|
+
await client.workspaces.delete(accountId);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Field definitions
|
|
267
|
+
|
|
268
|
+
Custom field types used by `collect`-method assignments.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
await client.fields.create({ type: 'text', name: 'Contract Number' });
|
|
272
|
+
await client.fields.list({ include_inactive: true, include_standard: true });
|
|
273
|
+
await client.fields.get(fieldId);
|
|
274
|
+
await client.fields.update(fieldId, { name: 'Updated Name' });
|
|
275
|
+
await client.fields.delete(fieldId);
|
|
276
|
+
|
|
277
|
+
// Validate a single value (signer-access-code only required for signer-side calls)
|
|
278
|
+
await client.fields.validate(fieldId, '400.676.228-36', { signerAccessCode });
|
|
279
|
+
|
|
280
|
+
// Validate multiple values at once
|
|
281
|
+
await client.fields.validateMultiple(
|
|
282
|
+
[
|
|
283
|
+
{ field_id: 'f1', value: '1111111111111' },
|
|
284
|
+
{ field_id: 'f2', value: 'foo@bar.com' },
|
|
285
|
+
],
|
|
286
|
+
{ signerAccessCode },
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Catalog of every field type the platform recognises
|
|
290
|
+
await client.fields.listTypes();
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Authentication / API key management
|
|
294
|
+
|
|
295
|
+
Most server-side integrations should just use `X-Api-Key` directly. Use these endpoints when you need to bootstrap a session for a human user.
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const { access_token, user, accounts } = await client.auth.login('me@example.com', 'pw');
|
|
299
|
+
await client.auth.socialLogin({ provider: 'google', token: 'google-id-token', has_accepted_terms: true });
|
|
300
|
+
|
|
301
|
+
// Personal API key
|
|
302
|
+
await client.auth.createApiKey('current-password');
|
|
303
|
+
await client.auth.getApiKey(); // → { api_key: '****...nBNr' } or null
|
|
304
|
+
await client.auth.deleteApiKey();
|
|
305
|
+
|
|
306
|
+
// Password lifecycle
|
|
307
|
+
await client.auth.changePassword({ email, password: 'current', new_password: 'next' });
|
|
308
|
+
await client.auth.requestPasswordReset('me@example.com');
|
|
309
|
+
await client.auth.resetPassword({ email, token: 'tk', new_password: 'next' });
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Webhooks
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
await client.webhooks.register({
|
|
316
|
+
url: 'https://example.com/webhooks/assinafy',
|
|
317
|
+
email: 'admin@example.com',
|
|
318
|
+
// events defaults to the current SDK default set below
|
|
319
|
+
events: [
|
|
320
|
+
'document_ready',
|
|
321
|
+
'document_prepared',
|
|
322
|
+
'signer_signed_document',
|
|
323
|
+
'signer_rejected_document',
|
|
324
|
+
'document_processing_failed',
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await client.webhooks.get(); // current subscription or null
|
|
329
|
+
await client.webhooks.inactivate();
|
|
330
|
+
await client.webhooks.delete();
|
|
331
|
+
await client.webhooks.listEventTypes();
|
|
332
|
+
await client.webhooks.listDispatches({ delivered: false, page: 1, 'per-page': 20 });
|
|
333
|
+
await client.webhooks.retryDispatch(dispatchId);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Webhook verification
|
|
337
|
+
|
|
338
|
+
Webhook payloads are signed with HMAC-SHA256 of the raw body using the workspace `webhookSecret`. Assinafy sends the hex digest in the `X-Assinafy-Signature` header.
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
import express from 'express';
|
|
342
|
+
|
|
343
|
+
app.post('/webhooks/assinafy', express.raw({ type: 'application/json' }), (req, res) => {
|
|
344
|
+
const signature = req.header('x-assinafy-signature') ?? '';
|
|
345
|
+
const rawBody = req.body as Buffer;
|
|
346
|
+
|
|
347
|
+
if (!client.webhookVerifier.verify(rawBody, signature)) {
|
|
348
|
+
return res.status(401).send('Invalid signature');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const event = client.webhookVerifier.extractEvent(rawBody);
|
|
352
|
+
const type = client.webhookVerifier.getEventType(event);
|
|
353
|
+
const data = client.webhookVerifier.getEventData(event);
|
|
354
|
+
|
|
355
|
+
switch (type) {
|
|
356
|
+
case 'document_ready': break;
|
|
357
|
+
case 'signer_signed_document': break;
|
|
358
|
+
case 'signer_rejected_document': break;
|
|
359
|
+
case 'document_processing_failed':break;
|
|
360
|
+
}
|
|
361
|
+
res.sendStatus(200);
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Signer-side endpoints
|
|
366
|
+
|
|
367
|
+
For building custom signer portals. Every call requires the `signer-access-code` URL parameter that Assinafy emails/whatsapps to the signer.
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
await client.signerDocuments.self(accessCode);
|
|
371
|
+
await client.signerDocuments.acceptTerms(accessCode);
|
|
372
|
+
await client.signerDocuments.verifyEmail({ signerAccessCode: accessCode, verificationCode: '123456' });
|
|
373
|
+
|
|
374
|
+
await client.signerDocuments.getCurrent(signerId, accessCode);
|
|
375
|
+
const { data } = await client.signerDocuments.list(signerId, accessCode, { search: 'invoice' });
|
|
376
|
+
await client.signerDocuments.download(signerId, documentId, 'original', accessCode);
|
|
377
|
+
|
|
378
|
+
await client.signerDocuments.confirmData(documentId, accessCode, {
|
|
379
|
+
email: 'me@example.com',
|
|
380
|
+
whatsapp_phone_number: '+5548999990000',
|
|
381
|
+
has_accepted_terms: true,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Signature image management
|
|
385
|
+
await client.signerDocuments.uploadSignature(accessCode, pngBuffer, { imageType: 'signature' });
|
|
386
|
+
await client.signerDocuments.downloadSignature(accessCode, 'signature');
|
|
387
|
+
|
|
388
|
+
// Sign / decline
|
|
389
|
+
const assignment = await client.signerDocuments.getAssignment(accessCode);
|
|
390
|
+
await client.signerDocuments.sign(documentId, assignmentId, accessCode, [
|
|
391
|
+
{ itemId, fieldId, pageId, value: 'Signed by John' },
|
|
392
|
+
]);
|
|
393
|
+
await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'Not authorized');
|
|
394
|
+
|
|
395
|
+
// Bulk operations
|
|
396
|
+
await client.signerDocuments.signMultiple(['doc-1', 'doc-2'], accessCode);
|
|
397
|
+
await client.signerDocuments.declineMultiple(['doc-1'], 'Unfavorable terms', accessCode);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## High-level helper
|
|
401
|
+
|
|
402
|
+
Uploads a PDF, waits for processing, reuses or creates signers by email, and kicks off a virtual assignment.
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
const result = await client.uploadAndRequestSignatures({
|
|
406
|
+
source: { filePath: './contract.pdf' },
|
|
407
|
+
signers: [
|
|
408
|
+
{ name: 'John', email: 'john@example.com' },
|
|
409
|
+
{ name: 'Jane', email: 'jane@example.com', whatsapp_phone_number: '+5548999990000' },
|
|
410
|
+
],
|
|
411
|
+
message: 'Please sign',
|
|
412
|
+
metadata: { year: 2026 },
|
|
413
|
+
waitForReady: true,
|
|
414
|
+
expiresAt: '2026-12-31T00:00:00Z',
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
result.document; // IDocumentUploadResponse
|
|
418
|
+
result.assignment; // IAssignment
|
|
419
|
+
result.signer_ids; // string[]
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Errors
|
|
423
|
+
|
|
424
|
+
Every method rejects with an `AssinafyError` subclass.
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import { ApiError, ValidationError, NetworkError, AssinafyError } from '@assinafy/sdk';
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
await client.documents.upload({ filePath: './x.pdf' });
|
|
431
|
+
} catch (err) {
|
|
432
|
+
if (err instanceof ValidationError) {
|
|
433
|
+
console.error('Validation failed:', err.errors);
|
|
434
|
+
} else if (err instanceof ApiError) {
|
|
435
|
+
console.error(`API error ${err.statusCode}:`, err.responseData);
|
|
436
|
+
} else if (err instanceof NetworkError) {
|
|
437
|
+
console.error('Network error:', err.message);
|
|
438
|
+
} else if (err instanceof AssinafyError) {
|
|
439
|
+
console.error('SDK error:', err.message, err.context);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Live smoke test
|
|
445
|
+
|
|
446
|
+
A real-network test script under [`scripts/live-smoke.ts`](scripts/live-smoke.ts) exercises the full API. Use it to sanity-check a workspace before shipping.
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
ASSINAFY_API_KEY=… ASSINAFY_ACCOUNT_ID=… bun scripts/live-smoke.ts # read-only
|
|
450
|
+
ASSINAFY_API_KEY=… ASSINAFY_ACCOUNT_ID=… bun scripts/live-smoke.ts --write # also creates+deletes a signer
|
|
451
|
+
ASSINAFY_API_KEY=… ASSINAFY_ACCOUNT_ID=… bun scripts/live-smoke.ts --upload # also uploads+deletes a PDF
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Development
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
bun install # or npm install
|
|
458
|
+
bun test # runs bun:test suites (Bun is required for tests)
|
|
459
|
+
npm run typecheck # tsc --noEmit
|
|
460
|
+
npm run lint
|
|
461
|
+
npm run build # tsup → dist/ (CJS + ESM + .d.ts)
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## License
|
|
465
|
+
|
|
466
|
+
MIT
|