@dispatchtickets/sdk 0.1.0 → 0.5.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 +168 -9
- package/dist/index.cjs +509 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +780 -20
- package/dist/index.d.ts +780 -20
- package/dist/index.js +497 -68
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -44,16 +44,83 @@ const client = new DispatchTickets({
|
|
|
44
44
|
apiKey: 'sk_live_...', // Required
|
|
45
45
|
baseUrl: 'https://...', // Optional, default: production API
|
|
46
46
|
timeout: 30000, // Optional, request timeout in ms
|
|
47
|
-
maxRetries: 3, // Optional, retry count for failed requests
|
|
48
47
|
debug: false, // Optional, enable debug logging
|
|
48
|
+
fetch: customFetch, // Optional, custom fetch for testing
|
|
49
|
+
retry: { // Optional, fine-grained retry config
|
|
50
|
+
maxRetries: 3,
|
|
51
|
+
retryableStatuses: [429, 500, 502, 503, 504],
|
|
52
|
+
initialDelayMs: 1000,
|
|
53
|
+
maxDelayMs: 30000,
|
|
54
|
+
},
|
|
55
|
+
hooks: { // Optional, observability hooks
|
|
56
|
+
onRequest: (ctx) => console.log(`${ctx.method} ${ctx.url}`),
|
|
57
|
+
onResponse: (ctx) => console.log(`${ctx.status} in ${ctx.durationMs}ms`),
|
|
58
|
+
onError: (error) => Sentry.captureException(error),
|
|
59
|
+
onRetry: (ctx, error, delay) => console.log(`Retrying in ${delay}ms`),
|
|
60
|
+
},
|
|
49
61
|
});
|
|
50
62
|
```
|
|
51
63
|
|
|
64
|
+
### Request Cancellation
|
|
65
|
+
|
|
66
|
+
Cancel long-running requests with an AbortController:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
|
|
71
|
+
// Cancel after 5 seconds
|
|
72
|
+
setTimeout(() => controller.abort(), 5000);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const page = await client.tickets.listPage('ws_abc', {}, {
|
|
76
|
+
signal: controller.signal,
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error.message.includes('aborted')) {
|
|
80
|
+
console.log('Request was cancelled');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
52
85
|
## Resources
|
|
53
86
|
|
|
87
|
+
### Accounts
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Get current account
|
|
91
|
+
const account = await client.accounts.me();
|
|
92
|
+
|
|
93
|
+
// Get usage statistics
|
|
94
|
+
const usage = await client.accounts.getUsage();
|
|
95
|
+
console.log(`${usage.ticketsThisMonth}/${usage.plan?.ticketLimit} tickets used`);
|
|
96
|
+
|
|
97
|
+
// List API keys
|
|
98
|
+
const apiKeys = await client.accounts.listApiKeys();
|
|
99
|
+
|
|
100
|
+
// Create a new API key
|
|
101
|
+
const newKey = await client.accounts.createApiKey({
|
|
102
|
+
name: 'Production',
|
|
103
|
+
allBrands: true, // or brandIds: ['br_123']
|
|
104
|
+
});
|
|
105
|
+
console.log('Save this key:', newKey.key); // Only shown once!
|
|
106
|
+
|
|
107
|
+
// Update API key scope
|
|
108
|
+
await client.accounts.updateApiKeyScope('key_abc', {
|
|
109
|
+
allBrands: false,
|
|
110
|
+
brandIds: ['br_123', 'br_456'],
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Revoke an API key
|
|
114
|
+
await client.accounts.revokeApiKey('key_abc');
|
|
115
|
+
```
|
|
116
|
+
|
|
54
117
|
### Brands
|
|
55
118
|
|
|
56
119
|
```typescript
|
|
120
|
+
// Get inbound email address for a brand
|
|
121
|
+
const inboundEmail = client.brands.getInboundEmail('br_abc123');
|
|
122
|
+
// Returns: br_abc123@inbound.dispatchtickets.com
|
|
123
|
+
|
|
57
124
|
// Create a brand
|
|
58
125
|
const brand = await client.brands.create({
|
|
59
126
|
name: 'Acme Support',
|
|
@@ -268,30 +335,73 @@ await client.fields.delete('ws_abc123', 'ticket', 'order_id');
|
|
|
268
335
|
|
|
269
336
|
## Error Handling
|
|
270
337
|
|
|
338
|
+
Use type guards for clean error handling:
|
|
339
|
+
|
|
271
340
|
```typescript
|
|
272
341
|
import {
|
|
273
342
|
DispatchTickets,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
343
|
+
isNotFoundError,
|
|
344
|
+
isAuthenticationError,
|
|
345
|
+
isRateLimitError,
|
|
346
|
+
isValidationError,
|
|
278
347
|
} from '@dispatchtickets/sdk';
|
|
279
348
|
|
|
280
349
|
try {
|
|
281
350
|
await client.tickets.get('ws_abc123', 'tkt_invalid');
|
|
282
351
|
} catch (error) {
|
|
283
|
-
if (error
|
|
352
|
+
if (isNotFoundError(error)) {
|
|
284
353
|
console.log('Ticket not found');
|
|
285
|
-
|
|
354
|
+
console.log('Request ID:', error.requestId); // For debugging with support
|
|
355
|
+
} else if (isAuthenticationError(error)) {
|
|
286
356
|
console.log('Invalid API key');
|
|
287
|
-
} else if (error
|
|
357
|
+
} else if (isRateLimitError(error)) {
|
|
288
358
|
console.log(`Rate limited. Retry after ${error.retryAfter} seconds`);
|
|
289
|
-
|
|
359
|
+
console.log(`Limit: ${error.limit}, Remaining: ${error.remaining}`);
|
|
360
|
+
} else if (isValidationError(error)) {
|
|
290
361
|
console.log('Validation errors:', error.errors);
|
|
291
362
|
}
|
|
292
363
|
}
|
|
293
364
|
```
|
|
294
365
|
|
|
366
|
+
All errors include a `requestId` for debugging with support.
|
|
367
|
+
|
|
368
|
+
## Webhook Events
|
|
369
|
+
|
|
370
|
+
Handle webhook events with full type safety:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
import {
|
|
374
|
+
DispatchTickets,
|
|
375
|
+
parseWebhookEvent,
|
|
376
|
+
isTicketCreatedEvent,
|
|
377
|
+
isTicketUpdatedEvent,
|
|
378
|
+
isCommentCreatedEvent,
|
|
379
|
+
} from '@dispatchtickets/sdk';
|
|
380
|
+
|
|
381
|
+
// Parse and validate webhook payload
|
|
382
|
+
const event = parseWebhookEvent(req.body);
|
|
383
|
+
|
|
384
|
+
// Use type guards for type-safe event handling
|
|
385
|
+
if (isTicketCreatedEvent(event)) {
|
|
386
|
+
// event.data is typed as TicketCreatedData
|
|
387
|
+
console.log('New ticket:', event.data.title);
|
|
388
|
+
console.log('Priority:', event.data.priority);
|
|
389
|
+
console.log('Customer:', event.data.customerEmail);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (isTicketUpdatedEvent(event)) {
|
|
393
|
+
// event.data is typed as TicketUpdatedData
|
|
394
|
+
console.log('Ticket updated:', event.data.id);
|
|
395
|
+
console.log('Changed fields:', event.data.changes);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (isCommentCreatedEvent(event)) {
|
|
399
|
+
// event.data is typed as CommentCreatedData
|
|
400
|
+
console.log('New comment on', event.data.ticketNumber);
|
|
401
|
+
console.log('Author:', event.data.comment.authorType);
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
295
405
|
## Webhook Verification
|
|
296
406
|
|
|
297
407
|
```typescript
|
|
@@ -322,6 +432,55 @@ app.post('/webhooks', (req, res) => {
|
|
|
322
432
|
});
|
|
323
433
|
```
|
|
324
434
|
|
|
435
|
+
## Testing
|
|
436
|
+
|
|
437
|
+
Use the custom `fetch` option to mock API responses in tests:
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { DispatchTickets } from '@dispatchtickets/sdk';
|
|
441
|
+
import { vi } from 'vitest';
|
|
442
|
+
|
|
443
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
444
|
+
ok: true,
|
|
445
|
+
status: 200,
|
|
446
|
+
headers: { get: () => 'application/json' },
|
|
447
|
+
json: () => Promise.resolve([{ id: 'br_123', name: 'Test' }]),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const client = new DispatchTickets({
|
|
451
|
+
apiKey: 'sk_test_123',
|
|
452
|
+
fetch: mockFetch,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const brands = await client.brands.list();
|
|
456
|
+
expect(brands).toHaveLength(1);
|
|
457
|
+
expect(mockFetch).toHaveBeenCalled();
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Examples
|
|
461
|
+
|
|
462
|
+
See the `/examples` directory for complete working examples:
|
|
463
|
+
|
|
464
|
+
- **[express-webhook.ts](./examples/express-webhook.ts)** - Express.js webhook handler with signature verification
|
|
465
|
+
- **[nextjs-api-route.ts](./examples/nextjs-api-route.ts)** - Next.js App Router webhook handler
|
|
466
|
+
- **[basic-usage.ts](./examples/basic-usage.ts)** - Common SDK operations (tickets, comments, pagination)
|
|
467
|
+
|
|
468
|
+
## API Documentation
|
|
469
|
+
|
|
470
|
+
Generate TypeDoc API documentation locally:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
npm run docs
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
This creates a `docs/` folder with HTML documentation for all exported types and methods.
|
|
477
|
+
|
|
478
|
+
## Links
|
|
479
|
+
|
|
480
|
+
- [API Reference (Swagger)](https://dispatch-tickets-api.onrender.com/docs)
|
|
481
|
+
- [GitHub](https://github.com/Epic-Design-Labs/app-dispatchtickets-sdk)
|
|
482
|
+
- [Changelog](./CHANGELOG.md)
|
|
483
|
+
|
|
325
484
|
## License
|
|
326
485
|
|
|
327
486
|
MIT
|