@assinafy/sdk 1.4.0 → 2.0.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/README.md +105 -19
- package/dist/index.d.mts +572 -74
- package/dist/index.d.ts +572 -74
- package/dist/index.js +564 -115
- package/dist/index.mjs +562 -115
- package/package.json +24 -12
package/README.md
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for the [Assinafy API](https://api.assinafy.com.br/v1/docs) — a Brazilian digital signature platform.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Covers the server-side surface of the API: documents, signers, assignments, templates, tags, workspaces, webhooks, field definitions, authentication, public/signer-side flows, and the high-level `uploadAndRequestSignatures` helper.
|
|
6
|
+
|
|
7
|
+
Deliberately not wrapped: the browser-redirect OAuth endpoints (`/auth/authenticate`, `/auth/link-social-login`, `/login-callback`), which a server-side SDK cannot meaningfully drive, and the account `theme`/`logo` branding routes.
|
|
6
8
|
|
|
7
9
|
## Requirements
|
|
8
10
|
|
|
9
|
-
- Node.js
|
|
11
|
+
- Node.js 22+ for the built-in `FormData` / `Blob` APIs used by uploads. Tested
|
|
12
|
+
on 22 (maintenance LTS) and 24 (active LTS); Node 20 reached end-of-life in
|
|
13
|
+
April 2026 and is no longer supported.
|
|
10
14
|
- or Bun 1.0+
|
|
11
15
|
|
|
12
16
|
## Installation
|
|
@@ -66,11 +70,19 @@ new AssinafyClient({ token: 'jwt_xxx', accountId: 'acc_xxx' });
|
|
|
66
70
|
| `apiKey` | string | — | Preferred credential (sent as `X-Api-Key`). |
|
|
67
71
|
| `token` | string | — | Legacy access token (sent as `Bearer`). |
|
|
68
72
|
| `accountId` | string | — | Default workspace/account ID. |
|
|
69
|
-
| `baseUrl` | string | `https://api.assinafy.com.br/v1` | Override base URL.
|
|
73
|
+
| `baseUrl` | string | `https://api.assinafy.com.br/v1` | Override base URL (e.g. the sandbox). |
|
|
70
74
|
| `webhookSecret` | string | — | Shared secret used by `WebhookVerifier`. |
|
|
71
75
|
| `timeout` | number | `30000` | Request timeout in milliseconds. |
|
|
76
|
+
| `maxRetries` | number | `2` | Auto-retries on HTTP 429, honoring `Retry-After`. `0` disables. |
|
|
72
77
|
| `logger` | `Logger` | no-op | Optional `{debug,info,warn,error}` logger. |
|
|
73
78
|
|
|
79
|
+
### Rate limiting
|
|
80
|
+
|
|
81
|
+
The API allows ~120 requests/minute and returns `X-Rate-Limit-*` headers. On an
|
|
82
|
+
HTTP `429`, the client automatically retries up to `maxRetries` times, waiting
|
|
83
|
+
for the server-provided `Retry-After` (or `X-Rate-Limit-Reset`) delay before
|
|
84
|
+
each attempt. Only `429` is retried, so non-idempotent calls are safe.
|
|
85
|
+
|
|
74
86
|
### Factories
|
|
75
87
|
|
|
76
88
|
```ts
|
|
@@ -90,16 +102,16 @@ Every public endpoint documented in https://api.assinafy.com.br/v1/docs is cover
|
|
|
90
102
|
|
|
91
103
|
| Resource | Endpoints |
|
|
92
104
|
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
-
| `client.documents` | list, upload, details, activities, waitUntilReady, download, thumbnail, downloadPage, statuses, delete, verify, createFromTemplate, estimateCostFromTemplate,
|
|
105
|
+
| `client.documents` | list, **search**, upload, details, get, **rename**, activities, waitUntilReady, download, thumbnail, downloadPage, statuses, delete, verify, createFromTemplate, estimateCostFromTemplate, getPublic, sendToken, listTags, replaceTags, addTags, detachTag, isFullySigned, getSigningProgress |
|
|
94
106
|
| `client.signers` | create, get, list, update, delete, findByEmail |
|
|
95
|
-
| `client.assignments` | create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications
|
|
96
|
-
| `client.templates` | list, get, downloadPage
|
|
107
|
+
| `client.assignments` | **list**, create, estimateCost, resetExpiration, resendNotification, estimateResendCost, listWhatsAppNotifications |
|
|
108
|
+
| `client.templates` | **create**, list, get, **update**, **delete**, downloadPage |
|
|
97
109
|
| `client.tags` | list, create, update, delete |
|
|
98
110
|
| `client.workspaces` | create, list, get, update, delete |
|
|
99
|
-
| `client.webhooks` | register, get, inactivate,
|
|
111
|
+
| `client.webhooks` | register, get, inactivate, listEventTypes, listDispatches, retryDispatch |
|
|
100
112
|
| `client.fields` | create, list, get, update, delete, validate, validateMultiple, listTypes |
|
|
101
113
|
| `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
|
|
114
|
+
| `client.signerDocuments` | getCurrent, list, **search**, download, signMultiple, declineMultiple, self, acceptTerms, verifyEmail, confirmData, uploadSignature, downloadSignature, getAssignment, sign, decline |
|
|
103
115
|
| `client.webhookVerifier` | verify, extractEvent, getEventType, getEventData |
|
|
104
116
|
|
|
105
117
|
## Resources
|
|
@@ -112,17 +124,40 @@ Most account-scoped methods accept an optional `accountId` that overrides the cl
|
|
|
112
124
|
// Upload from a file path (recommended)
|
|
113
125
|
const doc = await client.documents.upload(
|
|
114
126
|
{ filePath: './contract.pdf' },
|
|
115
|
-
{ metadata: { type: 'service' } },
|
|
127
|
+
{ name: 'Service agreement', metadata: { type: 'service' } },
|
|
116
128
|
);
|
|
129
|
+
// `name` is optional and defaults to the file's own name. The API derives the
|
|
130
|
+
// display name from the uploaded filename and appends `.pdf` when absent, so
|
|
131
|
+
// the document above is stored as 'Service agreement.pdf'. Accents are
|
|
132
|
+
// transliterated by the API ('Contrato de Serviço' → 'Contrato de Servico.pdf').
|
|
133
|
+
// → {
|
|
134
|
+
// resource: 'document', id: '1031…', account_id: '102d…', template_id: null,
|
|
135
|
+
// name: 'contract.pdf', status: 'uploaded',
|
|
136
|
+
// artifacts: { original: 'https://…/download/original' },
|
|
137
|
+
// signing_url: 'https://app…/sign/1031…',
|
|
138
|
+
// pages: [], // populated once status reaches `metadata_ready`
|
|
139
|
+
// tags: [], is_closed: false, created_at: '2026-…', updated_at: '2026-…'
|
|
140
|
+
// }
|
|
117
141
|
|
|
118
142
|
// …or from a Buffer already in memory
|
|
119
143
|
await client.documents.upload({ buffer, fileName: 'contract.pdf' });
|
|
120
144
|
|
|
145
|
+
// List → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
|
|
121
146
|
const { data, meta } = await client.documents.list({ page: 1, per_page: 20, sort: '-created_at' });
|
|
147
|
+
|
|
148
|
+
// Search is the lightweight alternative to list: same item shape, but the API
|
|
149
|
+
// skips the expanded `assignment`/`pages`. Prefer it for name lookups.
|
|
150
|
+
const hits = await client.documents.search({ search: 'agreement', status: 'pending_signature', 'per-page': 20 });
|
|
151
|
+
|
|
122
152
|
await client.documents.details(doc.id);
|
|
123
153
|
await client.documents.activities(doc.id);
|
|
124
154
|
await client.documents.waitUntilReady(doc.id, { maxWaitMs: 30_000 });
|
|
125
155
|
|
|
156
|
+
// Rename. The API rejects this with 400 while the document is still in
|
|
157
|
+
// `metadata_processing`, so await waitUntilReady() first on a fresh upload.
|
|
158
|
+
// (Passing `name` to upload() avoids both the round-trip and the race.)
|
|
159
|
+
await client.documents.rename(doc.id, 'Signed service agreement.pdf');
|
|
160
|
+
|
|
126
161
|
await client.documents.download(doc.id, 'certificated'); // 'original' | 'certificated' | 'certificate-page' | 'bundle'
|
|
127
162
|
await client.documents.thumbnail(doc.id);
|
|
128
163
|
await client.documents.downloadPage(doc.id, pageId);
|
|
@@ -159,6 +194,9 @@ await client.signers.create({
|
|
|
159
194
|
whatsapp_phone_number: '+5548999990000',
|
|
160
195
|
cpf: '123.456.789-00', // optional Brazilian tax ID — non-digits are stripped automatically
|
|
161
196
|
});
|
|
197
|
+
// → { id: '19e6…', full_name: 'John Doe', email: 'john@example.com',
|
|
198
|
+
// whatsapp_phone_number: '+5548999990000', has_accepted_terms: false }
|
|
199
|
+
// (note: `cpf` is accepted on input but never echoed back by the API)
|
|
162
200
|
|
|
163
201
|
// `email` is optional — a WhatsApp-only signer is valid (at least one is required)
|
|
164
202
|
await client.signers.create({
|
|
@@ -186,6 +224,10 @@ When an `email` is supplied, `signers.create()` is idempotent by email, matching
|
|
|
186
224
|
### Assignments
|
|
187
225
|
|
|
188
226
|
```ts
|
|
227
|
+
// List every assignment in the workspace.
|
|
228
|
+
// → { data: IAssignment[], meta?: { current_page, per_page, total, last_page } }
|
|
229
|
+
const { data, meta } = await client.assignments.list({ page: 1, 'per-page': 20 });
|
|
230
|
+
|
|
189
231
|
// Signers may be ids or objects — the SDK normalises to the API shape.
|
|
190
232
|
await client.assignments.create(documentId, {
|
|
191
233
|
method: 'virtual',
|
|
@@ -204,40 +246,80 @@ await client.assignments.create(documentId, {
|
|
|
204
246
|
],
|
|
205
247
|
});
|
|
206
248
|
|
|
207
|
-
// Estimate cost (signers may omit `id` when only the channel matters)
|
|
249
|
+
// Estimate cost (signers may omit `id` when only the channel matters) → ICostEstimate
|
|
208
250
|
await client.assignments.estimateCost(documentId, { signers: ['signer-1'] });
|
|
209
251
|
await client.assignments.estimateCost(documentId, {
|
|
210
252
|
signers: [{ verification_method: 'Whatsapp' }],
|
|
211
253
|
});
|
|
254
|
+
// → {
|
|
255
|
+
// documents: 1, credits: 0, needs_extra_document: false, extra_document_cost: 0,
|
|
256
|
+
// total_credits: 0, breakdown: [], document_balance: 67, credit_balance: 0,
|
|
257
|
+
// has_sufficient_resources: true, blocking_reason: null, message: null
|
|
258
|
+
// }
|
|
212
259
|
|
|
213
260
|
await client.assignments.resetExpiration(documentId, assignmentId, '2025-06-30T00:00:00Z');
|
|
214
261
|
await client.assignments.resetExpiration(documentId, assignmentId, null); // remove expiration
|
|
262
|
+
|
|
215
263
|
await client.assignments.resendNotification(documentId, assignmentId, signerId);
|
|
264
|
+
// → { is_sent: true, document_id: '…', signer_id: '…' }
|
|
265
|
+
|
|
216
266
|
await client.assignments.estimateResendCost(documentId, assignmentId, signerId);
|
|
217
|
-
|
|
218
|
-
|
|
267
|
+
// → { total: 0, breakdown: [{ code: 'NotificationEmailResend', name: '…', cost: 0 }],
|
|
268
|
+
// credit_balance: 0, has_sufficient_credits: true }
|
|
269
|
+
|
|
270
|
+
await client.assignments.listWhatsAppNotifications(documentId, assignmentId); // → IWhatsAppNotification[]
|
|
219
271
|
```
|
|
220
272
|
|
|
273
|
+
The `create` response is an `IAssignment`: `{ id, method, signers: [...], items: [...], signing_urls: [{ signer_id, url }], … }`.
|
|
274
|
+
|
|
221
275
|
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
276
|
|
|
277
|
+
**Cancelling a signature request.** Assinafy has no workspace-side "cancel" endpoint. To stop a pending request either delete the document (when its status is deletable) or have the signer decline:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
await client.documents.delete(documentId); // workspace-side
|
|
281
|
+
await client.signerDocuments.decline(documentId, assignmentId, accessCode, 'No longer needed'); // signer-side
|
|
282
|
+
```
|
|
283
|
+
|
|
223
284
|
### Templates
|
|
224
285
|
|
|
225
286
|
```ts
|
|
287
|
+
// Create a template by uploading a PDF (multipart). The template starts in
|
|
288
|
+
// `Uploaded` status and becomes `Ready` once its pages are processed.
|
|
289
|
+
const created = await client.templates.create(
|
|
290
|
+
{ filePath: './nda.pdf' }, // or { buffer, fileName: 'nda.pdf' }
|
|
291
|
+
{ name: 'NDA template' },
|
|
292
|
+
);
|
|
293
|
+
// →
|
|
294
|
+
// {
|
|
295
|
+
// resource: 'template', id: '1032...', name: 'nda.pdf',
|
|
296
|
+
// document_name: 'nda.pdf', message: null, status: 'Uploaded',
|
|
297
|
+
// roles: [{ id: '1032...', name: 'TemplateEditor', assignment_type: 'Editor' }],
|
|
298
|
+
// pages: [], tags: [], created_at: '2026-…', updated_at: '2026-…'
|
|
299
|
+
// }
|
|
300
|
+
|
|
226
301
|
const { data, meta } = await client.templates.list({ search: 'NDA', per_page: 20 });
|
|
227
|
-
const template = await client.templates.get(
|
|
228
|
-
await client.templates.
|
|
302
|
+
const template = await client.templates.get(created.id); // includes pages[] + default_document_tags
|
|
303
|
+
await client.templates.update(created.id, { name: 'NDA v2', message: 'Please sign' });
|
|
304
|
+
await client.templates.downloadPage(created.id, template.pages![0].id); // → Buffer (JPEG)
|
|
305
|
+
await client.templates.delete(created.id);
|
|
229
306
|
|
|
230
|
-
// Create a document from a template (each signer maps to a template role)
|
|
307
|
+
// Create a *document* from a template (each signer maps to a template role)
|
|
231
308
|
await client.documents.createFromTemplate(
|
|
232
309
|
templateId,
|
|
233
310
|
[{ role_id: template.roles![0].id, id: signerId, verification_method: 'Email', notification_methods: ['Email'] }],
|
|
234
311
|
{ name: 'NDA - John Doe', message: 'Please sign at your earliest convenience.' },
|
|
235
312
|
);
|
|
236
313
|
|
|
237
|
-
// Estimate the cost before creating
|
|
314
|
+
// Estimate the cost before creating → ICostEstimate
|
|
238
315
|
await client.documents.estimateCostFromTemplate(templateId, [{ role_id: 'role_id', id: signerId }]);
|
|
316
|
+
// → { documents: 1, total_credits: 0, document_balance: 67, credit_balance: 0,
|
|
317
|
+
// has_sufficient_resources: true, blocking_reason: null, breakdown: [], … }
|
|
239
318
|
```
|
|
240
319
|
|
|
320
|
+
Template creation only uploads the PDF and provisions the default editor role —
|
|
321
|
+
configure roles/fields in the Assinafy editor (or the web UI) afterwards.
|
|
322
|
+
|
|
241
323
|
### Tags
|
|
242
324
|
|
|
243
325
|
Workspace-scoped labels that can be attached to documents and templates. Tag names are unique per workspace (case-insensitive).
|
|
@@ -326,8 +408,7 @@ await client.webhooks.register({
|
|
|
326
408
|
});
|
|
327
409
|
|
|
328
410
|
await client.webhooks.get(); // current subscription or null
|
|
329
|
-
await client.webhooks.inactivate();
|
|
330
|
-
await client.webhooks.delete();
|
|
411
|
+
await client.webhooks.inactivate(); // stop deliveries (no delete route exists)
|
|
331
412
|
await client.webhooks.listEventTypes();
|
|
332
413
|
await client.webhooks.listDispatches({ delivered: false, page: 1, 'per-page': 20 });
|
|
333
414
|
await client.webhooks.retryDispatch(dispatchId);
|
|
@@ -373,6 +454,8 @@ await client.signerDocuments.verifyEmail({ signerAccessCode: accessCode, verific
|
|
|
373
454
|
|
|
374
455
|
await client.signerDocuments.getCurrent(signerId, accessCode);
|
|
375
456
|
const { data } = await client.signerDocuments.list(signerId, accessCode, { search: 'invoice' });
|
|
457
|
+
// Signer-side counterpart of documents.search(), authorised by the access code.
|
|
458
|
+
const found = await client.signerDocuments.search(signerId, accessCode, 'invoice');
|
|
376
459
|
await client.signerDocuments.download(signerId, documentId, 'original', accessCode);
|
|
377
460
|
|
|
378
461
|
await client.signerDocuments.confirmData(documentId, accessCode, {
|
|
@@ -448,9 +531,12 @@ A real-network test script under [`scripts/live-smoke.ts`](scripts/live-smoke.ts
|
|
|
448
531
|
```bash
|
|
449
532
|
ASSINAFY_API_KEY=… ASSINAFY_ACCOUNT_ID=… bun scripts/live-smoke.ts # read-only
|
|
450
533
|
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+
|
|
534
|
+
ASSINAFY_API_KEY=… ASSINAFY_ACCOUNT_ID=… bun scripts/live-smoke.ts --upload # also uploads a PDF + a template, then deletes both
|
|
452
535
|
```
|
|
453
536
|
|
|
537
|
+
Set `ASSINAFY_BASE_URL=https://sandbox.assinafy.com.br/v1` to run it against the
|
|
538
|
+
sandbox instead of production.
|
|
539
|
+
|
|
454
540
|
## Development
|
|
455
541
|
|
|
456
542
|
```bash
|