@signingstudio/sdk 1.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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/dist/index.cjs +696 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +702 -0
- package/dist/index.d.ts +702 -0
- package/dist/index.js +660 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
import { request, FormData } from 'undici';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
|
|
4
|
+
interface HttpClientOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
maxRetries: number;
|
|
8
|
+
requestTimeoutMs: number;
|
|
9
|
+
userAgent: string;
|
|
10
|
+
/** Test seam — overridable fetch-like transport for the test suite. */
|
|
11
|
+
fetchImpl?: FetchFn;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Minimal fetch surface the SDK consumes. Undici's `request` fits this
|
|
15
|
+
* shape; tests use a mocked implementation.
|
|
16
|
+
*/
|
|
17
|
+
type FetchFn = typeof request;
|
|
18
|
+
/**
|
|
19
|
+
* Successful response with the envelope unwrapped and rate-limit metadata
|
|
20
|
+
* hoisted to typed fields for easy access.
|
|
21
|
+
*/
|
|
22
|
+
interface ApiResponse<T> {
|
|
23
|
+
data: T;
|
|
24
|
+
meta: Record<string, unknown>;
|
|
25
|
+
requestId: string | null;
|
|
26
|
+
rateLimit: {
|
|
27
|
+
remainingMinute: number | null;
|
|
28
|
+
remainingDay: number | null;
|
|
29
|
+
limitMinute: number | null;
|
|
30
|
+
limitDay: number | null;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
interface RequestOptions {
|
|
34
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
35
|
+
path: string;
|
|
36
|
+
query?: Record<string, string | number | boolean | undefined | null>;
|
|
37
|
+
json?: unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Multipart body — passed straight to undici. Provide a FormData with
|
|
40
|
+
* one entry per field; PDF upload fields must be named `file`.
|
|
41
|
+
*/
|
|
42
|
+
form?: FormData;
|
|
43
|
+
/**
|
|
44
|
+
* If true, the raw response body is returned as a Uint8Array instead of
|
|
45
|
+
* being decoded as JSON. Used for the `?redirect=1` PDF download endpoints.
|
|
46
|
+
*/
|
|
47
|
+
binary?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* HTTP transport for the SDK. Wraps undici with:
|
|
51
|
+
* - Authorization header injection
|
|
52
|
+
* - JSON / multipart body handling
|
|
53
|
+
* - Envelope unwrap ({ success, data, meta })
|
|
54
|
+
* - Status → typed error mapping
|
|
55
|
+
* - Bounded retry on 429 (with Retry-After ≤ 60s) + 5xx + network errors
|
|
56
|
+
*/
|
|
57
|
+
declare class HttpClient {
|
|
58
|
+
private readonly opts;
|
|
59
|
+
constructor(opts: HttpClientOptions);
|
|
60
|
+
/**
|
|
61
|
+
* JSON-returning request. Callers get the parsed data + envelope meta +
|
|
62
|
+
* rate-limit + request-id, or a typed error.
|
|
63
|
+
*/
|
|
64
|
+
requestJson<T>(req: RequestOptions): Promise<ApiResponse<T>>;
|
|
65
|
+
/**
|
|
66
|
+
* Binary-returning request (PDF download). Skips JSON parsing; returns
|
|
67
|
+
* the raw body as a Uint8Array.
|
|
68
|
+
*/
|
|
69
|
+
requestBinary(req: RequestOptions): Promise<Uint8Array>;
|
|
70
|
+
private execute;
|
|
71
|
+
private buildUrl;
|
|
72
|
+
private parseSuccess;
|
|
73
|
+
private readRateLimit;
|
|
74
|
+
private buildApiError;
|
|
75
|
+
private extractRetryAfter;
|
|
76
|
+
private headerString;
|
|
77
|
+
private parseIntHeader;
|
|
78
|
+
private readText;
|
|
79
|
+
private readBinary;
|
|
80
|
+
/**
|
|
81
|
+
* Exponential backoff (500 ms · 1 s · 2 s · 4 s) plus a small jitter to
|
|
82
|
+
* avoid herd effects when many callers retry at once.
|
|
83
|
+
*/
|
|
84
|
+
private backoffMs;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Response types for the Signing Studio v1 REST API.
|
|
89
|
+
*
|
|
90
|
+
* These interfaces mirror the exact JSON shapes the server returns. The
|
|
91
|
+
* runtime SDK does no coercion beyond parsing the outer `{ success, data,
|
|
92
|
+
* meta }` envelope — callers that receive an object shaped by these types
|
|
93
|
+
* are looking at what the server actually sent.
|
|
94
|
+
*
|
|
95
|
+
* Field-level nullability follows the server: `string | null` means the
|
|
96
|
+
* server may emit `null`; a missing `| null` means the server always emits
|
|
97
|
+
* the field. Optionality (`?`) is only used when the SDK builds a request
|
|
98
|
+
* object; response types are exhaustive.
|
|
99
|
+
*/
|
|
100
|
+
type DocumentStatus = "draft" | "sent" | "viewed" | "completed" | "declined" | "cancelled" | "expired";
|
|
101
|
+
type RecipientStatus = "pending" | "sent" | "viewed" | "signed" | "declined";
|
|
102
|
+
type ListView = "active" | "archived" | "deleted" | "all";
|
|
103
|
+
/**
|
|
104
|
+
* Fields on the list endpoint — a lighter subset than the detail view.
|
|
105
|
+
* See {@link DocumentDetail} for the full shape.
|
|
106
|
+
*/
|
|
107
|
+
interface DocumentListItem {
|
|
108
|
+
id: string;
|
|
109
|
+
title: string;
|
|
110
|
+
status: DocumentStatus;
|
|
111
|
+
file_name: string;
|
|
112
|
+
page_count: number;
|
|
113
|
+
created_at: string;
|
|
114
|
+
sent_at: string | null;
|
|
115
|
+
completed_at: string | null;
|
|
116
|
+
expires_at: string | null;
|
|
117
|
+
is_archived: 0 | 1;
|
|
118
|
+
is_deleted: 0 | 1;
|
|
119
|
+
template_id: string | null;
|
|
120
|
+
}
|
|
121
|
+
interface DocumentListParams {
|
|
122
|
+
status?: DocumentStatus;
|
|
123
|
+
view?: ListView;
|
|
124
|
+
limit?: number;
|
|
125
|
+
offset?: number;
|
|
126
|
+
}
|
|
127
|
+
interface DocumentListMeta {
|
|
128
|
+
total: number;
|
|
129
|
+
limit: number;
|
|
130
|
+
offset: number;
|
|
131
|
+
view: string;
|
|
132
|
+
}
|
|
133
|
+
interface DocumentListResponse {
|
|
134
|
+
data: DocumentListItem[];
|
|
135
|
+
meta: DocumentListMeta;
|
|
136
|
+
}
|
|
137
|
+
type FieldType = "signature" | "initials" | "text" | "date" | "checkbox" | "radio" | "dropdown" | "email" | "phone" | "label";
|
|
138
|
+
interface DocumentField {
|
|
139
|
+
id: string;
|
|
140
|
+
document_id: string;
|
|
141
|
+
field_type: FieldType;
|
|
142
|
+
page: number;
|
|
143
|
+
x: number;
|
|
144
|
+
y: number;
|
|
145
|
+
width: number;
|
|
146
|
+
height: number;
|
|
147
|
+
label: string | null;
|
|
148
|
+
name: string | null;
|
|
149
|
+
placeholder: string | null;
|
|
150
|
+
required: 0 | 1;
|
|
151
|
+
readonly: 0 | 1;
|
|
152
|
+
signer_index: number;
|
|
153
|
+
options: string | null;
|
|
154
|
+
value: string | null;
|
|
155
|
+
}
|
|
156
|
+
interface Recipient {
|
|
157
|
+
id: string;
|
|
158
|
+
name: string;
|
|
159
|
+
email: string;
|
|
160
|
+
phone: string | null;
|
|
161
|
+
signing_order: number;
|
|
162
|
+
status: RecipientStatus;
|
|
163
|
+
viewed_at: string | null;
|
|
164
|
+
signed_at: string | null;
|
|
165
|
+
declined_at: string | null;
|
|
166
|
+
decline_reason: string | null;
|
|
167
|
+
role: string | null;
|
|
168
|
+
delivery: Array<"email" | "sms">;
|
|
169
|
+
/** Always null on the detail view. Use `remind()` to mint a fresh URL. */
|
|
170
|
+
signing_url: null;
|
|
171
|
+
signing_url_expires_at: string | null;
|
|
172
|
+
signing_url_used: boolean;
|
|
173
|
+
}
|
|
174
|
+
interface DocumentTemplateSummary {
|
|
175
|
+
id: string;
|
|
176
|
+
name: string;
|
|
177
|
+
signer_count: number;
|
|
178
|
+
signers: TemplateSigner[];
|
|
179
|
+
delivery_methods: Array<"email" | "sms" | "webhook">;
|
|
180
|
+
webhook_id: number | null;
|
|
181
|
+
default_subject: string | null;
|
|
182
|
+
default_message: string | null;
|
|
183
|
+
}
|
|
184
|
+
interface DocumentDetail {
|
|
185
|
+
id: string;
|
|
186
|
+
title: string;
|
|
187
|
+
status: DocumentStatus;
|
|
188
|
+
message: string | null;
|
|
189
|
+
email_subject: string | null;
|
|
190
|
+
instructions: string | null;
|
|
191
|
+
email_from_name: string | null;
|
|
192
|
+
email_from_address: string | null;
|
|
193
|
+
email_body_template: string | null;
|
|
194
|
+
sms_body_template: string | null;
|
|
195
|
+
file_name: string;
|
|
196
|
+
file_size: number;
|
|
197
|
+
page_count: number;
|
|
198
|
+
expires_at: string | null;
|
|
199
|
+
sent_at: string | null;
|
|
200
|
+
completed_at: string | null;
|
|
201
|
+
created_at: string;
|
|
202
|
+
updated_at: string;
|
|
203
|
+
is_archived: 0 | 1;
|
|
204
|
+
is_deleted: 0 | 1;
|
|
205
|
+
reminder_enabled: 0 | 1;
|
|
206
|
+
reminder_interval_days: number | null;
|
|
207
|
+
last_reminder_sent_at: string | null;
|
|
208
|
+
created_by_name: string | null;
|
|
209
|
+
fields: DocumentField[];
|
|
210
|
+
recipients: Recipient[];
|
|
211
|
+
fileUrl: string;
|
|
212
|
+
template: DocumentTemplateSummary | null;
|
|
213
|
+
}
|
|
214
|
+
interface DocumentProgress {
|
|
215
|
+
document_id: string;
|
|
216
|
+
status: DocumentStatus;
|
|
217
|
+
sent_at: string | null;
|
|
218
|
+
completed_at: string | null;
|
|
219
|
+
expires_at: string | null;
|
|
220
|
+
progress: {
|
|
221
|
+
total: number;
|
|
222
|
+
signed: number;
|
|
223
|
+
declined: number;
|
|
224
|
+
viewed: number;
|
|
225
|
+
pending: number;
|
|
226
|
+
percent_complete: number;
|
|
227
|
+
};
|
|
228
|
+
recipients: Array<Pick<Recipient, "id" | "name" | "email" | "signing_order" | "status" | "viewed_at" | "signed_at" | "declined_at" | "decline_reason">>;
|
|
229
|
+
}
|
|
230
|
+
/** One row from the audit log. */
|
|
231
|
+
interface ActivityEntry {
|
|
232
|
+
id: number;
|
|
233
|
+
account_id: number;
|
|
234
|
+
user_id: number | null;
|
|
235
|
+
entity_type: "document" | "signing";
|
|
236
|
+
entity_id: number;
|
|
237
|
+
action: string;
|
|
238
|
+
metadata: Record<string, unknown> | null;
|
|
239
|
+
ip_address: string | null;
|
|
240
|
+
user_agent: string | null;
|
|
241
|
+
created_at: string;
|
|
242
|
+
user_name: string | null;
|
|
243
|
+
user_email: string | null;
|
|
244
|
+
}
|
|
245
|
+
interface SendPrefillValue {
|
|
246
|
+
/** Either `field_id` (uuid) OR `field_name` must be set. */
|
|
247
|
+
field_id?: string;
|
|
248
|
+
field_name?: string;
|
|
249
|
+
value: string;
|
|
250
|
+
}
|
|
251
|
+
interface SendRecipient {
|
|
252
|
+
name: string;
|
|
253
|
+
email: string;
|
|
254
|
+
phone?: string;
|
|
255
|
+
/** 0-indexed. First to sign in a sequential chain is `0`. */
|
|
256
|
+
signing_order?: number;
|
|
257
|
+
role?: string;
|
|
258
|
+
email_subject?: string;
|
|
259
|
+
email_body_template?: string;
|
|
260
|
+
sms_body_template?: string;
|
|
261
|
+
email_from_name?: string;
|
|
262
|
+
}
|
|
263
|
+
interface SendDocumentPayload {
|
|
264
|
+
template_id: string;
|
|
265
|
+
title: string;
|
|
266
|
+
message?: string;
|
|
267
|
+
subject?: string;
|
|
268
|
+
expires_at?: string;
|
|
269
|
+
recipients: SendRecipient[];
|
|
270
|
+
prefill_values?: SendPrefillValue[];
|
|
271
|
+
instructions?: string;
|
|
272
|
+
email_from_name?: string;
|
|
273
|
+
email_from_address?: string;
|
|
274
|
+
email_body_template?: string;
|
|
275
|
+
sms_body_template?: string;
|
|
276
|
+
}
|
|
277
|
+
interface RemindResponse {
|
|
278
|
+
success: true;
|
|
279
|
+
signing_url: string;
|
|
280
|
+
}
|
|
281
|
+
interface SuccessAck {
|
|
282
|
+
success: true;
|
|
283
|
+
}
|
|
284
|
+
interface DeleteResponse extends SuccessAck {
|
|
285
|
+
mode: "soft" | "hard";
|
|
286
|
+
}
|
|
287
|
+
interface PresignedUrl {
|
|
288
|
+
url: string;
|
|
289
|
+
}
|
|
290
|
+
interface TemplateSigner {
|
|
291
|
+
role?: string;
|
|
292
|
+
default_name?: string;
|
|
293
|
+
default_email?: string;
|
|
294
|
+
delivery?: Array<"email" | "sms">;
|
|
295
|
+
email_subject?: string;
|
|
296
|
+
email_body_template?: string;
|
|
297
|
+
sms_body_template?: string;
|
|
298
|
+
email_from_name?: string;
|
|
299
|
+
}
|
|
300
|
+
interface TemplateListItem {
|
|
301
|
+
id: string;
|
|
302
|
+
name: string;
|
|
303
|
+
description: string | null;
|
|
304
|
+
file_name: string;
|
|
305
|
+
page_count: number;
|
|
306
|
+
created_at: string;
|
|
307
|
+
signer_count: number;
|
|
308
|
+
delivery_methods: Array<"email" | "sms" | "webhook">;
|
|
309
|
+
is_archived: 0 | 1;
|
|
310
|
+
created_by_name: string | null;
|
|
311
|
+
}
|
|
312
|
+
interface TemplateField {
|
|
313
|
+
id: string;
|
|
314
|
+
field_type: FieldType;
|
|
315
|
+
page: number;
|
|
316
|
+
x: number;
|
|
317
|
+
y: number;
|
|
318
|
+
width: number;
|
|
319
|
+
height: number;
|
|
320
|
+
label: string | null;
|
|
321
|
+
name: string | null;
|
|
322
|
+
placeholder: string | null;
|
|
323
|
+
required: 0 | 1;
|
|
324
|
+
readonly: 0 | 1;
|
|
325
|
+
signer_index: number;
|
|
326
|
+
options: string | null;
|
|
327
|
+
}
|
|
328
|
+
interface TemplateDetail {
|
|
329
|
+
id: string;
|
|
330
|
+
name: string;
|
|
331
|
+
description: string | null;
|
|
332
|
+
file_key: string;
|
|
333
|
+
file_name: string;
|
|
334
|
+
file_size: number;
|
|
335
|
+
page_count: number;
|
|
336
|
+
signer_count: number;
|
|
337
|
+
signers: TemplateSigner[];
|
|
338
|
+
delivery_methods: Array<"email" | "sms" | "webhook">;
|
|
339
|
+
webhook_id: number | null;
|
|
340
|
+
default_subject: string | null;
|
|
341
|
+
default_message: string | null;
|
|
342
|
+
email_from_name: string | null;
|
|
343
|
+
email_from_address: string | null;
|
|
344
|
+
email_body_template: string | null;
|
|
345
|
+
sms_body_template: string | null;
|
|
346
|
+
default_instructions: string | null;
|
|
347
|
+
is_archived: 0 | 1;
|
|
348
|
+
is_deleted: 0 | 1;
|
|
349
|
+
created_at: string;
|
|
350
|
+
updated_at: string;
|
|
351
|
+
created_by_name: string | null;
|
|
352
|
+
fields: TemplateField[];
|
|
353
|
+
fileUrl: string;
|
|
354
|
+
}
|
|
355
|
+
/** Metadata payload accepted by create + update. */
|
|
356
|
+
interface TemplateMetadata {
|
|
357
|
+
name?: string;
|
|
358
|
+
description?: string;
|
|
359
|
+
signer_count?: number;
|
|
360
|
+
signers?: TemplateSigner[];
|
|
361
|
+
delivery_methods?: Array<"email" | "sms" | "webhook">;
|
|
362
|
+
webhook_id?: number | null;
|
|
363
|
+
default_subject?: string;
|
|
364
|
+
default_message?: string;
|
|
365
|
+
email_from_name?: string;
|
|
366
|
+
email_from_address?: string;
|
|
367
|
+
email_body_template?: string;
|
|
368
|
+
sms_body_template?: string;
|
|
369
|
+
default_instructions?: string;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Field-editing shape. Wider than {@link TemplateField} — the input allows
|
|
373
|
+
* `options` as a real array (server-side it's persisted as a JSON string).
|
|
374
|
+
*/
|
|
375
|
+
interface TemplateFieldInput {
|
|
376
|
+
field_type: FieldType;
|
|
377
|
+
page: number;
|
|
378
|
+
x: number;
|
|
379
|
+
y: number;
|
|
380
|
+
width: number;
|
|
381
|
+
height: number;
|
|
382
|
+
label?: string;
|
|
383
|
+
name?: string;
|
|
384
|
+
placeholder?: string;
|
|
385
|
+
required?: boolean;
|
|
386
|
+
readonly?: boolean;
|
|
387
|
+
signer_index?: number;
|
|
388
|
+
options?: string[];
|
|
389
|
+
}
|
|
390
|
+
interface TemplateHistoryEntry {
|
|
391
|
+
id: number;
|
|
392
|
+
file_name: string;
|
|
393
|
+
file_size: number;
|
|
394
|
+
page_count: number;
|
|
395
|
+
replaced_at: string;
|
|
396
|
+
replaced_by_name: string | null;
|
|
397
|
+
}
|
|
398
|
+
type WebhookEvent = "document.sent" | "document.viewed" | "document.signed" | "document.declined" | "document.completed";
|
|
399
|
+
interface WebhookPayload {
|
|
400
|
+
event: WebhookEvent;
|
|
401
|
+
data: {
|
|
402
|
+
document: {
|
|
403
|
+
id: string;
|
|
404
|
+
title: string;
|
|
405
|
+
status: DocumentStatus;
|
|
406
|
+
template_id: string | null;
|
|
407
|
+
sent_at: string | null;
|
|
408
|
+
completed_at: string | null;
|
|
409
|
+
};
|
|
410
|
+
download_url: string | null;
|
|
411
|
+
download_expires_in_seconds: number;
|
|
412
|
+
recipients: Array<Pick<Recipient, "id" | "name" | "email" | "signing_order" | "status" | "viewed_at" | "signed_at" | "declined_at" | "decline_reason">>;
|
|
413
|
+
[k: string]: unknown;
|
|
414
|
+
};
|
|
415
|
+
timestamp: string;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Documents resource — send documents from templates, poll status,
|
|
420
|
+
* download completed PDFs, remind signers.
|
|
421
|
+
*
|
|
422
|
+
* Every method returns the parsed data payload. If you need the full
|
|
423
|
+
* envelope (rate-limit headers, request id) drop down to
|
|
424
|
+
* `client.http.requestJson`.
|
|
425
|
+
*/
|
|
426
|
+
declare class Documents {
|
|
427
|
+
private readonly http;
|
|
428
|
+
constructor(http: HttpClient);
|
|
429
|
+
/**
|
|
430
|
+
* List documents. All parameters are optional.
|
|
431
|
+
*
|
|
432
|
+
* The `view` toggle controls which lifecycle bucket to read from:
|
|
433
|
+
* - `active` (default) — non-archived, non-deleted
|
|
434
|
+
* - `archived` — archived only
|
|
435
|
+
* - `deleted` — soft-deleted only
|
|
436
|
+
* - `all` — active + archived, still excludes trash
|
|
437
|
+
*/
|
|
438
|
+
list(params?: DocumentListParams): Promise<DocumentListResponse>;
|
|
439
|
+
/**
|
|
440
|
+
* Send a new document built from a template.
|
|
441
|
+
*
|
|
442
|
+
* Server-side rules to be aware of (surfaced as {@link import("../errors/index.js").ValidationError}):
|
|
443
|
+
* - `template_id` is required.
|
|
444
|
+
* - At least one recipient must be supplied.
|
|
445
|
+
* - `prefill_values[]` needs either `field_id` or `field_name` + `value`.
|
|
446
|
+
* - Signature / initials fields can't be prefilled.
|
|
447
|
+
* - Any template field that is both required AND readonly must be prefilled.
|
|
448
|
+
*
|
|
449
|
+
* Successful sends count against the account's monthly document quota.
|
|
450
|
+
*/
|
|
451
|
+
send(payload: SendDocumentPayload): Promise<DocumentDetail>;
|
|
452
|
+
get(id: string): Promise<DocumentDetail>;
|
|
453
|
+
/**
|
|
454
|
+
* Delete a document. `hard: true` also purges the file from storage.
|
|
455
|
+
* Only valid on cancelled / completed / declined / expired documents;
|
|
456
|
+
* cancel first if the document is still in flight.
|
|
457
|
+
*/
|
|
458
|
+
delete(id: string, options?: {
|
|
459
|
+
hard?: boolean;
|
|
460
|
+
}): Promise<DeleteResponse>;
|
|
461
|
+
restore(id: string): Promise<SuccessAck>;
|
|
462
|
+
archive(id: string): Promise<SuccessAck>;
|
|
463
|
+
unarchive(id: string): Promise<SuccessAck>;
|
|
464
|
+
cancel(id: string): Promise<SuccessAck>;
|
|
465
|
+
/**
|
|
466
|
+
* Read the current signing progress — cheaper than {@link get} when
|
|
467
|
+
* polling. Returns overall counts plus per-recipient state, no field
|
|
468
|
+
* or template data.
|
|
469
|
+
*/
|
|
470
|
+
progress(id: string): Promise<DocumentProgress>;
|
|
471
|
+
/**
|
|
472
|
+
* Get a presigned download URL for the document PDF. For completed
|
|
473
|
+
* documents this is the flattened final PDF plus the audit-trail page;
|
|
474
|
+
* while in flight it's the in-progress snapshot (or source PDF if no
|
|
475
|
+
* signatures yet).
|
|
476
|
+
*/
|
|
477
|
+
downloadUrl(id: string): Promise<PresignedUrl>;
|
|
478
|
+
/**
|
|
479
|
+
* Fetch the document PDF as raw bytes in one call (follows the presigned
|
|
480
|
+
* URL redirect server-side). Handy for saving straight to disk.
|
|
481
|
+
*/
|
|
482
|
+
downloadPdf(id: string): Promise<Uint8Array>;
|
|
483
|
+
activity(id: string): Promise<ActivityEntry[]>;
|
|
484
|
+
/**
|
|
485
|
+
* Re-send the signing invite (or first invite, if it hasn't gone out
|
|
486
|
+
* yet) to a specific recipient. Response includes a freshly-minted
|
|
487
|
+
* signing URL — this is the only endpoint where a URL is returned in
|
|
488
|
+
* plaintext, so capture it if you need to relay it out-of-band.
|
|
489
|
+
*/
|
|
490
|
+
remind(documentId: string, recipientId: string): Promise<RemindResponse>;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** Anything the SDK accepts as a PDF payload. */
|
|
494
|
+
type PdfInput = string | Uint8Array | Buffer | Blob | {
|
|
495
|
+
path: string;
|
|
496
|
+
} | {
|
|
497
|
+
data: Uint8Array | Buffer;
|
|
498
|
+
filename?: string;
|
|
499
|
+
contentType?: string;
|
|
500
|
+
};
|
|
501
|
+
/**
|
|
502
|
+
* Templates resource — create / update / version PDF templates.
|
|
503
|
+
*
|
|
504
|
+
* PDF upload constraints (server-enforced, 400 on violation):
|
|
505
|
+
* - Max 50 MB per file
|
|
506
|
+
* - `application/pdf` only
|
|
507
|
+
* - Multipart form field must be `file` — the SDK sets this for you
|
|
508
|
+
*/
|
|
509
|
+
declare class Templates {
|
|
510
|
+
private readonly http;
|
|
511
|
+
constructor(http: HttpClient);
|
|
512
|
+
list(): Promise<TemplateListItem[]>;
|
|
513
|
+
/**
|
|
514
|
+
* Create a new template from a PDF plus metadata.
|
|
515
|
+
*
|
|
516
|
+
* The `pdf` argument can be a filesystem path, a Uint8Array/Buffer of
|
|
517
|
+
* raw bytes, a Blob, or an object with an explicit filename. See
|
|
518
|
+
* {@link PdfInput}.
|
|
519
|
+
*/
|
|
520
|
+
create(pdf: PdfInput, metadata: TemplateMetadata, options?: {
|
|
521
|
+
filename?: string;
|
|
522
|
+
}): Promise<TemplateDetail>;
|
|
523
|
+
get(id: string): Promise<TemplateDetail>;
|
|
524
|
+
update(id: string, metadata: TemplateMetadata): Promise<TemplateDetail>;
|
|
525
|
+
/**
|
|
526
|
+
* Soft-delete. There is no hard-delete on this route; contact support
|
|
527
|
+
* if you need permanent removal.
|
|
528
|
+
*/
|
|
529
|
+
delete(id: string): Promise<SuccessAck>;
|
|
530
|
+
pdfUrl(id: string): Promise<PresignedUrl>;
|
|
531
|
+
downloadPdf(id: string): Promise<Uint8Array>;
|
|
532
|
+
/**
|
|
533
|
+
* Replace the template's PDF. Prior versions are kept in history —
|
|
534
|
+
* download via {@link historyPdfUrl}.
|
|
535
|
+
*/
|
|
536
|
+
replacePdf(id: string, pdf: PdfInput, options?: {
|
|
537
|
+
filename?: string;
|
|
538
|
+
}): Promise<TemplateDetail>;
|
|
539
|
+
/**
|
|
540
|
+
* Replace ALL fields on the template. The server deletes existing rows
|
|
541
|
+
* and inserts the supplied set — the SDK does not support partial
|
|
542
|
+
* updates because the server doesn't.
|
|
543
|
+
*/
|
|
544
|
+
setFields(id: string, fields: TemplateFieldInput[]): Promise<TemplateField[]>;
|
|
545
|
+
duplicate(id: string): Promise<TemplateDetail>;
|
|
546
|
+
archive(id: string): Promise<SuccessAck>;
|
|
547
|
+
history(id: string): Promise<TemplateHistoryEntry[]>;
|
|
548
|
+
historyPdfUrl(templateId: string, historyId: number): Promise<PresignedUrl>;
|
|
549
|
+
downloadHistoryPdf(templateId: string, historyId: number): Promise<Uint8Array>;
|
|
550
|
+
/**
|
|
551
|
+
* Build the multipart form: PDF blob under field name `file` (required
|
|
552
|
+
* by the server) + JSON-encoded scalar/array metadata fields.
|
|
553
|
+
*
|
|
554
|
+
* Nested arrays/objects (like `signers`, `delivery_methods`) are
|
|
555
|
+
* serialized with JSON.stringify so they survive the form-data trip —
|
|
556
|
+
* the server route unwraps them.
|
|
557
|
+
*/
|
|
558
|
+
private buildPdfForm;
|
|
559
|
+
private resolvePdf;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
interface ClientOptions {
|
|
563
|
+
/** Your API key from Signing Studio → Settings → API Keys. Required. */
|
|
564
|
+
apiKey: string;
|
|
565
|
+
/** Override for staging / self-hosted deployments. Default: production. */
|
|
566
|
+
baseUrl?: string;
|
|
567
|
+
/** How many times to retry transient failures (429 short Retry-After, 5xx, network). Default 3. */
|
|
568
|
+
maxRetries?: number;
|
|
569
|
+
/** Full request-timeout in milliseconds — generous by default so a 50 MB PDF upload has time to finish. */
|
|
570
|
+
requestTimeoutMs?: number;
|
|
571
|
+
/** Custom User-Agent — replace or extend with your app's identifier. */
|
|
572
|
+
userAgent?: string;
|
|
573
|
+
/**
|
|
574
|
+
* Test-only seam. Replace the transport with a mocked implementation
|
|
575
|
+
* in the test suite. Undici's `request` fits by default.
|
|
576
|
+
*/
|
|
577
|
+
fetchImpl?: FetchFn;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Top-level entry point for the Signing Studio Node.js SDK.
|
|
581
|
+
*
|
|
582
|
+
* import { SigningStudio } from "@signingstudio/sdk";
|
|
583
|
+
*
|
|
584
|
+
* const client = new SigningStudio({ apiKey: process.env.SIGNING_STUDIO_API_KEY! });
|
|
585
|
+
* const doc = await client.documents.send({
|
|
586
|
+
* template_id: "…",
|
|
587
|
+
* title: "MSA — Acme",
|
|
588
|
+
* recipients: [{ name: "Alex", email: "alex@acme.com" }],
|
|
589
|
+
* });
|
|
590
|
+
*
|
|
591
|
+
* The Client is safe to reuse across concurrent requests. Undici keeps an
|
|
592
|
+
* internal connection pool per host, which is the recommended shape for
|
|
593
|
+
* long-running processes (workers, cron, servers).
|
|
594
|
+
*/
|
|
595
|
+
declare class SigningStudio {
|
|
596
|
+
readonly documents: Documents;
|
|
597
|
+
readonly templates: Templates;
|
|
598
|
+
/** Escape hatch — the raw HTTP layer for callers that need to consume
|
|
599
|
+
* headers (rate-limit remaining, request id) alongside the parsed data. */
|
|
600
|
+
readonly http: HttpClient;
|
|
601
|
+
constructor(options: ClientOptions);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Verify an incoming Signing Studio webhook delivery.
|
|
606
|
+
*
|
|
607
|
+
* The server signs the raw JSON body with HMAC-SHA256 keyed on the
|
|
608
|
+
* webhook's secret and sends it as `X-DDS-Signature: sha256=<hex>`.
|
|
609
|
+
*
|
|
610
|
+
* Usage (Express-style example):
|
|
611
|
+
*
|
|
612
|
+
* import express from "express";
|
|
613
|
+
* import { verifyWebhook } from "@signingstudio/sdk";
|
|
614
|
+
*
|
|
615
|
+
* const app = express();
|
|
616
|
+
* app.post("/webhooks/signing-studio",
|
|
617
|
+
* express.raw({ type: "application/json" }), // do NOT pre-parse
|
|
618
|
+
* (req, res) => {
|
|
619
|
+
* const ok = verifyWebhook(req.body, req.get("X-DDS-Signature"), SECRET);
|
|
620
|
+
* if (!ok) return res.status(401).end();
|
|
621
|
+
* const payload = JSON.parse(req.body.toString("utf8"));
|
|
622
|
+
* // ...act on payload.event
|
|
623
|
+
* res.status(200).end();
|
|
624
|
+
* }
|
|
625
|
+
* );
|
|
626
|
+
*
|
|
627
|
+
* You MUST pass the RAW body — a parsed & re-serialized body will fail
|
|
628
|
+
* verification, even if it looks identical.
|
|
629
|
+
*/
|
|
630
|
+
declare function verifyWebhook(rawBody: string | Uint8Array | Buffer, header: string | null | undefined, secret: string): boolean;
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Every exception the SDK throws extends {@link SigningStudioError}, so
|
|
634
|
+
* integrators can catch a single type at the outer boundary of their code
|
|
635
|
+
* and inspect the subclass for specifics.
|
|
636
|
+
*
|
|
637
|
+
* try {
|
|
638
|
+
* await client.documents.send(payload);
|
|
639
|
+
* } catch (err) {
|
|
640
|
+
* if (err instanceof ValidationError) { /* field-map available */ }
|
|
641
|
+
* if (err instanceof RateLimitError) { /* retryAfter, window */ }
|
|
642
|
+
* if (err instanceof SigningStudioError) { /* fallback */ }
|
|
643
|
+
* throw err;
|
|
644
|
+
* }
|
|
645
|
+
*/
|
|
646
|
+
declare class SigningStudioError extends Error {
|
|
647
|
+
constructor(message: string, options?: {
|
|
648
|
+
cause?: unknown;
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
interface ApiErrorInit {
|
|
652
|
+
statusCode: number;
|
|
653
|
+
requestId?: string | null;
|
|
654
|
+
body?: Record<string, unknown> | null;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Non-2xx HTTP response from the API. Carries the parsed error envelope,
|
|
658
|
+
* the request id (for support tickets), and the status code (for
|
|
659
|
+
* programmatic branching).
|
|
660
|
+
*/
|
|
661
|
+
declare class ApiError extends SigningStudioError {
|
|
662
|
+
readonly statusCode: number;
|
|
663
|
+
readonly requestId: string | null;
|
|
664
|
+
readonly body: Record<string, unknown> | null;
|
|
665
|
+
constructor(message: string, init: ApiErrorInit);
|
|
666
|
+
}
|
|
667
|
+
/** HTTP 401 — API key missing, unknown, or the account behind it is inactive. */
|
|
668
|
+
declare class AuthenticationError extends ApiError {
|
|
669
|
+
constructor(message: string, init: ApiErrorInit);
|
|
670
|
+
}
|
|
671
|
+
/** HTTP 404 — resource does not exist under the caller's account. */
|
|
672
|
+
declare class NotFoundError extends ApiError {
|
|
673
|
+
constructor(message: string, init: ApiErrorInit);
|
|
674
|
+
}
|
|
675
|
+
interface ValidationErrorInit extends ApiErrorInit {
|
|
676
|
+
errors: Record<string, string[]>;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* HTTP 422 — Zod-level or business-rule validation failure. `errors` is
|
|
680
|
+
* the field-name → messages map from the server; empty when the failure
|
|
681
|
+
* was purely business-rule (e.g. prefill of a signature field).
|
|
682
|
+
*/
|
|
683
|
+
declare class ValidationError extends ApiError {
|
|
684
|
+
readonly errors: Record<string, string[]>;
|
|
685
|
+
constructor(message: string, init: ValidationErrorInit);
|
|
686
|
+
}
|
|
687
|
+
interface RateLimitErrorInit extends ApiErrorInit {
|
|
688
|
+
retryAfter: number | null;
|
|
689
|
+
window: string | null;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* HTTP 429 — rate limit or monthly quota. `retryAfter` is present for
|
|
693
|
+
* sliding-window rate limits (seconds until safe to retry) and null for
|
|
694
|
+
* monthly quota exhaustion.
|
|
695
|
+
*/
|
|
696
|
+
declare class RateLimitError extends ApiError {
|
|
697
|
+
readonly retryAfter: number | null;
|
|
698
|
+
readonly window: string | null;
|
|
699
|
+
constructor(message: string, init: RateLimitErrorInit);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export { type ActivityEntry, ApiError, AuthenticationError, type ClientOptions, type DeleteResponse, type DocumentDetail, type DocumentField, type DocumentListItem, type DocumentListMeta, type DocumentListParams, type DocumentListResponse, type DocumentProgress, type DocumentStatus, type DocumentTemplateSummary, Documents, type FieldType, type ListView, NotFoundError, type PdfInput, type PresignedUrl, RateLimitError, type Recipient, type RecipientStatus, type RemindResponse, type SendDocumentPayload, type SendPrefillValue, type SendRecipient, SigningStudio, SigningStudioError, type SuccessAck, type TemplateDetail, type TemplateField, type TemplateFieldInput, type TemplateHistoryEntry, type TemplateListItem, type TemplateMetadata, type TemplateSigner, Templates, ValidationError, type WebhookEvent, type WebhookPayload, verifyWebhook };
|