@rynko/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/README.md +735 -0
- package/dist/index.d.mts +616 -0
- package/dist/index.d.ts +616 -0
- package/dist/index.js +640 -0
- package/dist/index.mjs +604 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
# @rynko/sdk
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [Rynko](https://rynko.dev) - the document generation platform with unified template design for PDF and Excel documents.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@rynko/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Features](#features)
|
|
13
|
+
- [Authentication](#authentication)
|
|
14
|
+
- [Document Generation](#document-generation)
|
|
15
|
+
- [Generate PDF](#generate-pdf)
|
|
16
|
+
- [Generate Excel](#generate-excel)
|
|
17
|
+
- [Generate with Options](#generate-with-options)
|
|
18
|
+
- [Batch Generation](#batch-generation)
|
|
19
|
+
- [Wait for Completion](#wait-for-completion)
|
|
20
|
+
- [Document Jobs](#document-jobs)
|
|
21
|
+
- [Get Job Status](#get-job-status)
|
|
22
|
+
- [List Jobs](#list-jobs)
|
|
23
|
+
- [Templates](#templates)
|
|
24
|
+
- [List Templates](#list-templates)
|
|
25
|
+
- [Get Template Details](#get-template-details)
|
|
26
|
+
- [Webhooks](#webhooks)
|
|
27
|
+
- [List Webhooks](#list-webhooks)
|
|
28
|
+
- [Verify Webhook Signatures](#verify-webhook-signatures)
|
|
29
|
+
- [Configuration](#configuration)
|
|
30
|
+
- [Error Handling](#error-handling)
|
|
31
|
+
- [TypeScript Support](#typescript-support)
|
|
32
|
+
- [API Reference](#api-reference)
|
|
33
|
+
- [Requirements](#requirements)
|
|
34
|
+
- [Support](#support)
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @rynko/sdk
|
|
40
|
+
# or
|
|
41
|
+
yarn add @rynko/sdk
|
|
42
|
+
# or
|
|
43
|
+
pnpm add @rynko/sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { Rynko } from '@rynko/sdk';
|
|
50
|
+
|
|
51
|
+
const rynko = new Rynko({
|
|
52
|
+
apiKey: process.env.RYNKO_API_KEY!,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Generate a PDF document (async - returns job info immediately)
|
|
56
|
+
const job = await rynko.documents.generatePdf({
|
|
57
|
+
templateId: 'tmpl_invoice',
|
|
58
|
+
variables: {
|
|
59
|
+
customerName: 'John Doe',
|
|
60
|
+
invoiceNumber: 'INV-001',
|
|
61
|
+
total: 150.00,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
console.log('Job ID:', job.jobId);
|
|
66
|
+
console.log('Status:', job.status); // 'queued'
|
|
67
|
+
|
|
68
|
+
// Wait for completion to get the download URL
|
|
69
|
+
const completed = await rynko.documents.waitForCompletion(job.jobId);
|
|
70
|
+
console.log('Download URL:', completed.downloadUrl);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Features
|
|
74
|
+
|
|
75
|
+
- **Full TypeScript support** with comprehensive type definitions
|
|
76
|
+
- **Promise-based API** for modern async/await usage
|
|
77
|
+
- **PDF generation** - Generate PDF documents from templates
|
|
78
|
+
- **Excel generation** - Generate Excel spreadsheets from templates
|
|
79
|
+
- **Batch generation** - Generate multiple documents in a single request
|
|
80
|
+
- **Workspace support** - Generate documents in specific workspaces
|
|
81
|
+
- **Webhook verification** - Secure HMAC signature verification for incoming webhooks
|
|
82
|
+
- **Polling utility** - Built-in `waitForCompletion()` method with configurable timeout
|
|
83
|
+
- **Automatic retries** - Configurable retry logic for transient failures
|
|
84
|
+
|
|
85
|
+
## Authentication
|
|
86
|
+
|
|
87
|
+
### Get an API Key
|
|
88
|
+
|
|
89
|
+
1. Log in to your [Rynko Dashboard](https://app.rynko.dev)
|
|
90
|
+
2. Navigate to **Settings** → **API Keys**
|
|
91
|
+
3. Click **Create API Key**
|
|
92
|
+
4. Copy the key and store it securely (it won't be shown again)
|
|
93
|
+
|
|
94
|
+
### Initialize the Client
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { Rynko } from '@rynko/sdk';
|
|
98
|
+
|
|
99
|
+
// Using environment variable (recommended)
|
|
100
|
+
const rynko = new Rynko({
|
|
101
|
+
apiKey: process.env.RYNKO_API_KEY!,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Verify authentication
|
|
105
|
+
const user = await rynko.me();
|
|
106
|
+
console.log('Authenticated as:', user.email);
|
|
107
|
+
console.log('Team:', user.teamName);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Verify API Key
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Check if API key is valid
|
|
114
|
+
const isValid = await rynko.verifyApiKey();
|
|
115
|
+
console.log('API Key valid:', isValid);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Document Generation
|
|
119
|
+
|
|
120
|
+
Document generation in Rynko is **asynchronous**. When you call a generate method, the job is queued for processing and you receive a job ID immediately. Use `waitForCompletion()` to poll until the document is ready.
|
|
121
|
+
|
|
122
|
+
### Generate PDF
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Queue PDF generation
|
|
126
|
+
const job = await rynko.documents.generatePdf({
|
|
127
|
+
templateId: 'tmpl_invoice',
|
|
128
|
+
variables: {
|
|
129
|
+
invoiceNumber: 'INV-001',
|
|
130
|
+
customerName: 'John Doe',
|
|
131
|
+
customerEmail: 'john@example.com',
|
|
132
|
+
items: [
|
|
133
|
+
{ description: 'Product A', quantity: 2, price: 50.00 },
|
|
134
|
+
{ description: 'Product B', quantity: 1, price: 50.00 },
|
|
135
|
+
],
|
|
136
|
+
subtotal: 150.00,
|
|
137
|
+
tax: 15.00,
|
|
138
|
+
total: 165.00,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
console.log('Job queued:', job.jobId);
|
|
143
|
+
console.log('Status:', job.status); // 'queued'
|
|
144
|
+
|
|
145
|
+
// Wait for completion
|
|
146
|
+
const completed = await rynko.documents.waitForCompletion(job.jobId);
|
|
147
|
+
console.log('Download URL:', completed.downloadUrl);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Generate Excel
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const job = await rynko.documents.generateExcel({
|
|
154
|
+
templateId: 'tmpl_sales_report',
|
|
155
|
+
variables: {
|
|
156
|
+
reportTitle: 'Q1 2026 Sales Report',
|
|
157
|
+
reportDate: '2026-03-31',
|
|
158
|
+
salesData: [
|
|
159
|
+
{ region: 'North', q1: 125000, q2: 0, q3: 0, q4: 0 },
|
|
160
|
+
{ region: 'South', q1: 98000, q2: 0, q3: 0, q4: 0 },
|
|
161
|
+
{ region: 'East', q1: 145000, q2: 0, q3: 0, q4: 0 },
|
|
162
|
+
{ region: 'West', q1: 112000, q2: 0, q3: 0, q4: 0 },
|
|
163
|
+
],
|
|
164
|
+
totalSales: 480000,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const completed = await rynko.documents.waitForCompletion(job.jobId);
|
|
169
|
+
console.log('Excel file ready:', completed.downloadUrl);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Generate with Options
|
|
173
|
+
|
|
174
|
+
The `generate()` method supports all document formats and advanced options:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const job = await rynko.documents.generate({
|
|
178
|
+
// Required
|
|
179
|
+
templateId: 'tmpl_contract',
|
|
180
|
+
format: 'pdf', // 'pdf' | 'excel' | 'csv'
|
|
181
|
+
|
|
182
|
+
// Template variables
|
|
183
|
+
variables: {
|
|
184
|
+
contractNumber: 'CTR-2026-001',
|
|
185
|
+
clientName: 'Acme Corporation',
|
|
186
|
+
startDate: '2026-02-01',
|
|
187
|
+
endDate: '2027-01-31',
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// Optional settings
|
|
191
|
+
filename: 'contract-acme-2026', // Custom filename (without extension)
|
|
192
|
+
workspaceId: 'ws_abc123', // Generate in specific workspace
|
|
193
|
+
webhookUrl: 'https://your-app.com/webhooks/document-ready', // Webhook notification
|
|
194
|
+
metadata: { // Custom metadata (passed to webhook)
|
|
195
|
+
orderId: 'ORD-12345',
|
|
196
|
+
userId: 'user_abc',
|
|
197
|
+
},
|
|
198
|
+
useDraft: false, // Use draft template version (for testing)
|
|
199
|
+
useCredit: false, // Force use of purchased credits
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Batch Generation
|
|
204
|
+
|
|
205
|
+
Generate multiple documents from a single template:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Each object in the documents array contains variables for one document
|
|
209
|
+
const batch = await rynko.documents.generateBatch({
|
|
210
|
+
templateId: 'tmpl_invoice',
|
|
211
|
+
format: 'pdf',
|
|
212
|
+
documents: [
|
|
213
|
+
{
|
|
214
|
+
invoiceNumber: 'INV-001',
|
|
215
|
+
customerName: 'John Doe',
|
|
216
|
+
total: 150.00,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
invoiceNumber: 'INV-002',
|
|
220
|
+
customerName: 'Jane Smith',
|
|
221
|
+
total: 275.50,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
invoiceNumber: 'INV-003',
|
|
225
|
+
customerName: 'Bob Wilson',
|
|
226
|
+
total: 89.99,
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
webhookUrl: 'https://your-app.com/webhooks/batch-complete',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
console.log('Batch ID:', batch.batchId);
|
|
233
|
+
console.log('Total jobs:', batch.totalJobs); // 3
|
|
234
|
+
console.log('Status:', batch.status); // 'queued'
|
|
235
|
+
console.log('Estimated wait:', batch.estimatedWaitSeconds, 'seconds');
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Wait for Completion
|
|
239
|
+
|
|
240
|
+
The `waitForCompletion()` method polls the job status until it completes or fails:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Default settings (1 second interval, 30 second timeout)
|
|
244
|
+
const completed = await rynko.documents.waitForCompletion(job.jobId);
|
|
245
|
+
|
|
246
|
+
// Custom polling settings
|
|
247
|
+
const completed = await rynko.documents.waitForCompletion(job.jobId, {
|
|
248
|
+
pollInterval: 2000, // Check every 2 seconds
|
|
249
|
+
timeout: 60000, // Wait up to 60 seconds
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Check result
|
|
253
|
+
if (completed.status === 'completed') {
|
|
254
|
+
console.log('Download URL:', completed.downloadUrl);
|
|
255
|
+
console.log('File size:', completed.fileSize, 'bytes');
|
|
256
|
+
console.log('Expires at:', completed.downloadUrlExpiresAt);
|
|
257
|
+
} else if (completed.status === 'failed') {
|
|
258
|
+
console.error('Generation failed:', completed.errorMessage);
|
|
259
|
+
console.error('Error code:', completed.errorCode);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Document Jobs
|
|
264
|
+
|
|
265
|
+
### Get Job Status
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const job = await rynko.documents.getJob('job_abc123');
|
|
269
|
+
|
|
270
|
+
console.log('Status:', job.status);
|
|
271
|
+
// Possible values: 'queued' | 'processing' | 'completed' | 'failed'
|
|
272
|
+
|
|
273
|
+
console.log('Template:', job.templateName);
|
|
274
|
+
console.log('Format:', job.format);
|
|
275
|
+
console.log('Created:', job.createdAt);
|
|
276
|
+
|
|
277
|
+
if (job.status === 'completed') {
|
|
278
|
+
console.log('Download URL:', job.downloadUrl);
|
|
279
|
+
console.log('File size:', job.fileSize);
|
|
280
|
+
console.log('URL expires:', job.downloadUrlExpiresAt);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (job.status === 'failed') {
|
|
284
|
+
console.log('Error:', job.errorMessage);
|
|
285
|
+
console.log('Error code:', job.errorCode);
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### List Jobs
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// List recent jobs with pagination
|
|
293
|
+
const { data: jobs, meta } = await rynko.documents.listJobs({
|
|
294
|
+
limit: 20,
|
|
295
|
+
page: 1,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
console.log('Total jobs:', meta.total);
|
|
299
|
+
console.log('Pages:', meta.totalPages);
|
|
300
|
+
|
|
301
|
+
for (const job of jobs) {
|
|
302
|
+
console.log(`${job.jobId}: ${job.status} - ${job.templateName}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Filter by status
|
|
306
|
+
const { data: completedJobs } = await rynko.documents.listJobs({
|
|
307
|
+
status: 'completed',
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Filter by format
|
|
311
|
+
const { data: pdfJobs } = await rynko.documents.listJobs({
|
|
312
|
+
format: 'pdf',
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Filter by template
|
|
316
|
+
const { data: invoiceJobs } = await rynko.documents.listJobs({
|
|
317
|
+
templateId: 'tmpl_invoice',
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Filter by workspace
|
|
321
|
+
const { data: workspaceJobs } = await rynko.documents.listJobs({
|
|
322
|
+
workspaceId: 'ws_abc123',
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Filter by date range
|
|
326
|
+
const { data: recentJobs } = await rynko.documents.listJobs({
|
|
327
|
+
dateFrom: new Date('2026-01-01'),
|
|
328
|
+
dateTo: new Date('2026-01-31'),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Combine filters
|
|
332
|
+
const { data: filteredJobs } = await rynko.documents.listJobs({
|
|
333
|
+
status: 'completed',
|
|
334
|
+
format: 'pdf',
|
|
335
|
+
templateId: 'tmpl_invoice',
|
|
336
|
+
limit: 50,
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Templates
|
|
341
|
+
|
|
342
|
+
### List Templates
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// List all templates
|
|
346
|
+
const { data: templates, meta } = await rynko.templates.list();
|
|
347
|
+
|
|
348
|
+
console.log('Total templates:', meta.total);
|
|
349
|
+
|
|
350
|
+
for (const template of templates) {
|
|
351
|
+
console.log(`${template.id}: ${template.name} (${template.type})`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Paginated list
|
|
355
|
+
const { data: page2 } = await rynko.templates.list({
|
|
356
|
+
page: 2,
|
|
357
|
+
limit: 10,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Search by name
|
|
361
|
+
const { data: invoiceTemplates } = await rynko.templates.list({
|
|
362
|
+
search: 'invoice',
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// List PDF templates only
|
|
366
|
+
const { data: pdfTemplates } = await rynko.templates.listPdf();
|
|
367
|
+
|
|
368
|
+
// List Excel templates only
|
|
369
|
+
const { data: excelTemplates } = await rynko.templates.listExcel();
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Get Template Details
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// Get template by ID (supports UUID, shortId, or slug)
|
|
376
|
+
const template = await rynko.templates.get('tmpl_invoice');
|
|
377
|
+
|
|
378
|
+
console.log('Template:', template.name);
|
|
379
|
+
console.log('Type:', template.type); // 'pdf' | 'excel'
|
|
380
|
+
console.log('Description:', template.description);
|
|
381
|
+
console.log('Created:', template.createdAt);
|
|
382
|
+
console.log('Updated:', template.updatedAt);
|
|
383
|
+
|
|
384
|
+
// View template variables
|
|
385
|
+
if (template.variables) {
|
|
386
|
+
console.log('\nVariables:');
|
|
387
|
+
for (const variable of template.variables) {
|
|
388
|
+
console.log(` ${variable.name} (${variable.type})`);
|
|
389
|
+
console.log(` Required: ${variable.required ?? false}`);
|
|
390
|
+
if (variable.defaultValue !== undefined) {
|
|
391
|
+
console.log(` Default: ${JSON.stringify(variable.defaultValue)}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Webhooks
|
|
398
|
+
|
|
399
|
+
Webhook subscriptions are managed through the [Rynko Dashboard](https://app.rynko.dev). The SDK provides read-only access to view webhooks and utilities for signature verification.
|
|
400
|
+
|
|
401
|
+
### List Webhooks
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
const { data: webhooks, meta } = await rynko.webhooks.list();
|
|
405
|
+
|
|
406
|
+
for (const webhook of webhooks) {
|
|
407
|
+
console.log(`${webhook.id}: ${webhook.url}`);
|
|
408
|
+
console.log(` Events: ${webhook.events.join(', ')}`);
|
|
409
|
+
console.log(` Active: ${webhook.isActive}`);
|
|
410
|
+
console.log(` Created: ${webhook.createdAt}`);
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Get Webhook Details
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const webhook = await rynko.webhooks.get('wh_abc123');
|
|
418
|
+
|
|
419
|
+
console.log('URL:', webhook.url);
|
|
420
|
+
console.log('Events:', webhook.events);
|
|
421
|
+
console.log('Active:', webhook.isActive);
|
|
422
|
+
console.log('Description:', webhook.description);
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Verify Webhook Signatures
|
|
426
|
+
|
|
427
|
+
When receiving webhooks, always verify the signature to ensure the request came from Rynko:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import { verifyWebhookSignature, WebhookSignatureError } from '@rynko/sdk';
|
|
431
|
+
|
|
432
|
+
// Express.js example
|
|
433
|
+
app.post('/webhooks/rynko', express.raw({ type: 'application/json' }), (req, res) => {
|
|
434
|
+
const signature = req.headers['x-rynko-signature'] as string;
|
|
435
|
+
const timestamp = req.headers['x-rynko-timestamp'] as string;
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const event = verifyWebhookSignature({
|
|
439
|
+
payload: req.body.toString(),
|
|
440
|
+
signature,
|
|
441
|
+
timestamp, // Optional but recommended for replay protection
|
|
442
|
+
secret: process.env.WEBHOOK_SECRET!,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Process the verified event
|
|
446
|
+
console.log('Event type:', event.type);
|
|
447
|
+
console.log('Event ID:', event.id);
|
|
448
|
+
console.log('Timestamp:', event.timestamp);
|
|
449
|
+
|
|
450
|
+
switch (event.type) {
|
|
451
|
+
case 'document.generated':
|
|
452
|
+
const { jobId, downloadUrl, templateId } = event.data;
|
|
453
|
+
console.log(`Document ${jobId} ready: ${downloadUrl}`);
|
|
454
|
+
// Download or process the document
|
|
455
|
+
break;
|
|
456
|
+
|
|
457
|
+
case 'document.failed':
|
|
458
|
+
const { jobId: failedJobId, error, errorCode } = event.data;
|
|
459
|
+
console.error(`Document ${failedJobId} failed: ${error}`);
|
|
460
|
+
// Handle failure (retry, notify user, etc.)
|
|
461
|
+
break;
|
|
462
|
+
|
|
463
|
+
case 'document.downloaded':
|
|
464
|
+
const { jobId: downloadedJobId } = event.data;
|
|
465
|
+
console.log(`Document ${downloadedJobId} was downloaded`);
|
|
466
|
+
break;
|
|
467
|
+
|
|
468
|
+
default:
|
|
469
|
+
console.log('Unhandled event type:', event.type);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
res.status(200).send('OK');
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (error instanceof WebhookSignatureError) {
|
|
475
|
+
console.error('Invalid webhook signature:', error.message);
|
|
476
|
+
res.status(401).send('Invalid signature');
|
|
477
|
+
} else {
|
|
478
|
+
console.error('Webhook processing error:', error);
|
|
479
|
+
res.status(500).send('Internal error');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### Webhook Event Types
|
|
486
|
+
|
|
487
|
+
| Event | Description | Payload |
|
|
488
|
+
|-------|-------------|---------|
|
|
489
|
+
| `document.generated` | Document successfully generated | `jobId`, `templateId`, `format`, `downloadUrl`, `fileSize` |
|
|
490
|
+
| `document.failed` | Document generation failed | `jobId`, `templateId`, `error`, `errorCode` |
|
|
491
|
+
| `document.downloaded` | Document was downloaded | `jobId`, `downloadedAt` |
|
|
492
|
+
|
|
493
|
+
#### Webhook Headers
|
|
494
|
+
|
|
495
|
+
Rynko sends these headers with each webhook request:
|
|
496
|
+
|
|
497
|
+
| Header | Description |
|
|
498
|
+
|--------|-------------|
|
|
499
|
+
| `X-Rynko-Signature` | HMAC-SHA256 signature (format: `v1=<hex>`) |
|
|
500
|
+
| `X-Rynko-Timestamp` | Unix timestamp when the webhook was sent |
|
|
501
|
+
| `X-Rynko-Event-Id` | Unique event identifier |
|
|
502
|
+
| `X-Rynko-Event-Type` | Event type (e.g., `document.generated`) |
|
|
503
|
+
|
|
504
|
+
#### Low-Level Signature Utilities
|
|
505
|
+
|
|
506
|
+
For advanced use cases, you can use the low-level signature utilities:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
import { parseSignatureHeader, computeSignature } from '@rynko/sdk';
|
|
510
|
+
|
|
511
|
+
// Parse the signature header
|
|
512
|
+
const { timestamp, signatures } = parseSignatureHeader(signatureHeader);
|
|
513
|
+
|
|
514
|
+
// Compute expected signature
|
|
515
|
+
const expectedSignature = computeSignature(timestamp, payload, secret);
|
|
516
|
+
|
|
517
|
+
// Compare signatures
|
|
518
|
+
const isValid = signatures.some(sig => sig === expectedSignature);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Configuration
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
const rynko = new Rynko({
|
|
525
|
+
// Required: Your API key
|
|
526
|
+
apiKey: process.env.RYNKO_API_KEY!,
|
|
527
|
+
|
|
528
|
+
// Optional: Custom base URL (default: https://api.rynko.dev)
|
|
529
|
+
baseUrl: 'https://api.rynko.dev',
|
|
530
|
+
|
|
531
|
+
// Optional: Request timeout in milliseconds (default: 30000)
|
|
532
|
+
timeout: 30000,
|
|
533
|
+
|
|
534
|
+
// Optional: Custom headers for all requests
|
|
535
|
+
headers: {
|
|
536
|
+
'X-Custom-Header': 'value',
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Environment Variables
|
|
542
|
+
|
|
543
|
+
We recommend using environment variables for configuration:
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
# .env
|
|
547
|
+
RYNKO_API_KEY=your_api_key_here
|
|
548
|
+
WEBHOOK_SECRET=your_webhook_secret_here
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
import 'dotenv/config';
|
|
553
|
+
import { Rynko } from '@rynko/sdk';
|
|
554
|
+
|
|
555
|
+
const rynko = new Rynko({
|
|
556
|
+
apiKey: process.env.RYNKO_API_KEY!,
|
|
557
|
+
});
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
## Error Handling
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
import { Rynko, RynkoError } from '@rynko/sdk';
|
|
564
|
+
|
|
565
|
+
const rynko = new Rynko({ apiKey: 'your_api_key' });
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
const job = await rynko.documents.generatePdf({
|
|
569
|
+
templateId: 'invalid_template',
|
|
570
|
+
variables: {},
|
|
571
|
+
});
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (error instanceof RynkoError) {
|
|
574
|
+
console.error('API Error:', error.message);
|
|
575
|
+
console.error('Error Code:', error.code);
|
|
576
|
+
console.error('Status Code:', error.statusCode);
|
|
577
|
+
|
|
578
|
+
// Handle specific error codes
|
|
579
|
+
switch (error.code) {
|
|
580
|
+
case 'ERR_TMPL_001':
|
|
581
|
+
console.error('Template not found');
|
|
582
|
+
break;
|
|
583
|
+
case 'ERR_TMPL_003':
|
|
584
|
+
console.error('Template validation failed');
|
|
585
|
+
break;
|
|
586
|
+
case 'ERR_QUOTA_001':
|
|
587
|
+
console.error('Document quota exceeded - upgrade your plan');
|
|
588
|
+
break;
|
|
589
|
+
case 'ERR_AUTH_001':
|
|
590
|
+
console.error('Invalid API key');
|
|
591
|
+
break;
|
|
592
|
+
case 'ERR_AUTH_004':
|
|
593
|
+
console.error('API key expired or revoked');
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
console.error('Unknown error');
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
// Network error or other non-API error
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### Common Error Codes
|
|
606
|
+
|
|
607
|
+
| Code | Description |
|
|
608
|
+
|------|-------------|
|
|
609
|
+
| `ERR_AUTH_001` | Invalid credentials / API key |
|
|
610
|
+
| `ERR_AUTH_004` | Token expired or revoked |
|
|
611
|
+
| `ERR_TMPL_001` | Template not found |
|
|
612
|
+
| `ERR_TMPL_003` | Template validation failed |
|
|
613
|
+
| `ERR_DOC_001` | Document job not found |
|
|
614
|
+
| `ERR_DOC_004` | Document generation failed |
|
|
615
|
+
| `ERR_QUOTA_001` | Document quota exceeded |
|
|
616
|
+
| `ERR_QUOTA_002` | Rate limit exceeded |
|
|
617
|
+
|
|
618
|
+
## TypeScript Support
|
|
619
|
+
|
|
620
|
+
This SDK is written in TypeScript and includes comprehensive type definitions:
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
import {
|
|
624
|
+
Rynko,
|
|
625
|
+
RynkoError,
|
|
626
|
+
verifyWebhookSignature,
|
|
627
|
+
WebhookSignatureError,
|
|
628
|
+
} from '@rynko/sdk';
|
|
629
|
+
|
|
630
|
+
import type {
|
|
631
|
+
// Configuration
|
|
632
|
+
RynkoConfig,
|
|
633
|
+
|
|
634
|
+
// Document types
|
|
635
|
+
GenerateDocumentOptions,
|
|
636
|
+
GenerateBatchOptions,
|
|
637
|
+
GenerateDocumentResponse,
|
|
638
|
+
GenerateBatchResponse,
|
|
639
|
+
DocumentJob,
|
|
640
|
+
DocumentJobStatus,
|
|
641
|
+
ListDocumentJobsOptions,
|
|
642
|
+
|
|
643
|
+
// Template types
|
|
644
|
+
Template,
|
|
645
|
+
TemplateVariable,
|
|
646
|
+
ListTemplatesOptions,
|
|
647
|
+
|
|
648
|
+
// Webhook types
|
|
649
|
+
WebhookSubscription,
|
|
650
|
+
WebhookEventType,
|
|
651
|
+
WebhookEvent,
|
|
652
|
+
|
|
653
|
+
// Response types
|
|
654
|
+
ApiResponse,
|
|
655
|
+
PaginationMeta,
|
|
656
|
+
ApiError,
|
|
657
|
+
|
|
658
|
+
// User types
|
|
659
|
+
User,
|
|
660
|
+
} from '@rynko/sdk';
|
|
661
|
+
|
|
662
|
+
// Type-safe document generation
|
|
663
|
+
const options: GenerateDocumentOptions = {
|
|
664
|
+
templateId: 'tmpl_invoice',
|
|
665
|
+
format: 'pdf',
|
|
666
|
+
variables: {
|
|
667
|
+
invoiceNumber: 'INV-001',
|
|
668
|
+
items: [{ name: 'Widget', price: 29.99 }],
|
|
669
|
+
},
|
|
670
|
+
workspaceId: 'ws_abc123', // Optional
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const result: GenerateDocumentResponse = await rynko.documents.generate(options);
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
## API Reference
|
|
677
|
+
|
|
678
|
+
### Client Methods
|
|
679
|
+
|
|
680
|
+
| Method | Returns | Description |
|
|
681
|
+
|--------|---------|-------------|
|
|
682
|
+
| `me()` | `Promise<User>` | Get current authenticated user |
|
|
683
|
+
| `verifyApiKey()` | `Promise<boolean>` | Verify API key is valid |
|
|
684
|
+
|
|
685
|
+
### Documents Resource
|
|
686
|
+
|
|
687
|
+
| Method | Returns | Description |
|
|
688
|
+
|--------|---------|-------------|
|
|
689
|
+
| `generate(options)` | `Promise<GenerateDocumentResponse>` | Generate a document (PDF, Excel, or CSV) |
|
|
690
|
+
| `generatePdf(options)` | `Promise<GenerateDocumentResponse>` | Generate a PDF document |
|
|
691
|
+
| `generateExcel(options)` | `Promise<GenerateDocumentResponse>` | Generate an Excel document |
|
|
692
|
+
| `generateBatch(options)` | `Promise<GenerateBatchResponse>` | Generate multiple documents |
|
|
693
|
+
| `getJob(jobId)` | `Promise<DocumentJob>` | Get document job by ID |
|
|
694
|
+
| `listJobs(options?)` | `Promise<{ data: DocumentJob[]; meta: PaginationMeta }>` | List/search document jobs |
|
|
695
|
+
| `waitForCompletion(jobId, options?)` | `Promise<DocumentJob>` | Poll until job completes or fails |
|
|
696
|
+
|
|
697
|
+
### Templates Resource
|
|
698
|
+
|
|
699
|
+
| Method | Returns | Description |
|
|
700
|
+
|--------|---------|-------------|
|
|
701
|
+
| `get(templateId)` | `Promise<Template>` | Get template by ID (UUID, shortId, or slug) |
|
|
702
|
+
| `list(options?)` | `Promise<{ data: Template[]; meta: PaginationMeta }>` | List all templates |
|
|
703
|
+
| `listPdf(options?)` | `Promise<{ data: Template[]; meta: PaginationMeta }>` | List PDF templates only |
|
|
704
|
+
| `listExcel(options?)` | `Promise<{ data: Template[]; meta: PaginationMeta }>` | List Excel templates only |
|
|
705
|
+
|
|
706
|
+
### Webhooks Resource
|
|
707
|
+
|
|
708
|
+
| Method | Returns | Description |
|
|
709
|
+
|--------|---------|-------------|
|
|
710
|
+
| `get(webhookId)` | `Promise<WebhookSubscription>` | Get webhook subscription by ID |
|
|
711
|
+
| `list()` | `Promise<{ data: WebhookSubscription[]; meta: PaginationMeta }>` | List all webhook subscriptions |
|
|
712
|
+
|
|
713
|
+
### Utilities
|
|
714
|
+
|
|
715
|
+
| Function | Returns | Description |
|
|
716
|
+
|----------|---------|-------------|
|
|
717
|
+
| `verifyWebhookSignature(options)` | `WebhookEvent` | Verify signature and parse webhook event |
|
|
718
|
+
| `parseSignatureHeader(header)` | `{ timestamp: string; signatures: string[] }` | Parse signature header |
|
|
719
|
+
| `computeSignature(timestamp, payload, secret)` | `string` | Compute expected signature |
|
|
720
|
+
|
|
721
|
+
## Requirements
|
|
722
|
+
|
|
723
|
+
- Node.js 18.0.0 or higher
|
|
724
|
+
- npm, yarn, or pnpm
|
|
725
|
+
|
|
726
|
+
## License
|
|
727
|
+
|
|
728
|
+
MIT
|
|
729
|
+
|
|
730
|
+
## Support
|
|
731
|
+
|
|
732
|
+
- **Documentation**: https://docs.rynko.dev/sdk/node
|
|
733
|
+
- **API Reference**: https://docs.rynko.dev/api
|
|
734
|
+
- **GitHub Issues**: https://github.com/rynko/sdk-node/issues
|
|
735
|
+
- **Email**: support@rynko.dev
|