@dispatchtickets/sdk 0.3.0 → 0.6.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 +57 -9
- package/dist/index.cjs +708 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +975 -15
- package/dist/index.d.ts +975 -15
- package/dist/index.js +699 -67
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,105 @@
|
|
|
2
2
|
* Custom fetch function type
|
|
3
3
|
*/
|
|
4
4
|
type FetchFunction = typeof fetch;
|
|
5
|
+
/**
|
|
6
|
+
* Request context passed to hooks
|
|
7
|
+
*/
|
|
8
|
+
interface RequestContext {
|
|
9
|
+
/** HTTP method */
|
|
10
|
+
method: string;
|
|
11
|
+
/** Full URL being requested */
|
|
12
|
+
url: string;
|
|
13
|
+
/** Request headers */
|
|
14
|
+
headers: Record<string, string>;
|
|
15
|
+
/** Request body (if any) */
|
|
16
|
+
body?: unknown;
|
|
17
|
+
/** Retry attempt number (0 = first attempt) */
|
|
18
|
+
attempt: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Response context passed to hooks
|
|
22
|
+
*/
|
|
23
|
+
interface ResponseContext {
|
|
24
|
+
/** The request that was made */
|
|
25
|
+
request: RequestContext;
|
|
26
|
+
/** HTTP status code */
|
|
27
|
+
status: number;
|
|
28
|
+
/** Response headers */
|
|
29
|
+
headers: Headers;
|
|
30
|
+
/** Request ID from server */
|
|
31
|
+
requestId?: string;
|
|
32
|
+
/** Rate limit info from server */
|
|
33
|
+
rateLimit?: RateLimitInfo;
|
|
34
|
+
/** Duration of the request in milliseconds */
|
|
35
|
+
durationMs: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Retry configuration options
|
|
39
|
+
*/
|
|
40
|
+
interface RetryConfig {
|
|
41
|
+
/**
|
|
42
|
+
* Maximum number of retry attempts
|
|
43
|
+
* @default 3
|
|
44
|
+
*/
|
|
45
|
+
maxRetries?: number;
|
|
46
|
+
/**
|
|
47
|
+
* HTTP status codes that should trigger a retry
|
|
48
|
+
* @default [429, 500, 502, 503, 504]
|
|
49
|
+
*/
|
|
50
|
+
retryableStatuses?: number[];
|
|
51
|
+
/**
|
|
52
|
+
* Whether to retry on network errors
|
|
53
|
+
* @default true
|
|
54
|
+
*/
|
|
55
|
+
retryOnNetworkError?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether to retry on timeout errors
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
retryOnTimeout?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Initial delay between retries in milliseconds
|
|
63
|
+
* @default 1000
|
|
64
|
+
*/
|
|
65
|
+
initialDelayMs?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Maximum delay between retries in milliseconds
|
|
68
|
+
* @default 30000
|
|
69
|
+
*/
|
|
70
|
+
maxDelayMs?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Multiplier for exponential backoff
|
|
73
|
+
* @default 2
|
|
74
|
+
*/
|
|
75
|
+
backoffMultiplier?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Jitter factor (0-1) to add randomness to delays
|
|
78
|
+
* @default 0.25
|
|
79
|
+
*/
|
|
80
|
+
jitter?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Hooks for observability and customization
|
|
84
|
+
*/
|
|
85
|
+
interface Hooks {
|
|
86
|
+
/**
|
|
87
|
+
* Called before each request is sent
|
|
88
|
+
* Can modify the request or throw to abort
|
|
89
|
+
*/
|
|
90
|
+
onRequest?: (context: RequestContext) => void | Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Called after each successful response
|
|
93
|
+
*/
|
|
94
|
+
onResponse?: (context: ResponseContext) => void | Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Called when an error occurs (before retry)
|
|
97
|
+
*/
|
|
98
|
+
onError?: (error: Error, context: RequestContext) => void | Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Called before each retry attempt
|
|
101
|
+
*/
|
|
102
|
+
onRetry?: (context: RequestContext, error: Error, delayMs: number) => void | Promise<void>;
|
|
103
|
+
}
|
|
5
104
|
interface HttpClientConfig {
|
|
6
105
|
baseUrl: string;
|
|
7
106
|
apiKey: string;
|
|
@@ -12,6 +111,14 @@ interface HttpClientConfig {
|
|
|
12
111
|
* Custom fetch implementation for testing/mocking
|
|
13
112
|
*/
|
|
14
113
|
fetch?: FetchFunction;
|
|
114
|
+
/**
|
|
115
|
+
* Fine-grained retry configuration
|
|
116
|
+
*/
|
|
117
|
+
retry?: RetryConfig;
|
|
118
|
+
/**
|
|
119
|
+
* Hooks for observability
|
|
120
|
+
*/
|
|
121
|
+
hooks?: Hooks;
|
|
15
122
|
}
|
|
16
123
|
interface RequestOptions {
|
|
17
124
|
method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
@@ -20,40 +127,103 @@ interface RequestOptions {
|
|
|
20
127
|
query?: Record<string, string | number | boolean | undefined>;
|
|
21
128
|
headers?: Record<string, string>;
|
|
22
129
|
idempotencyKey?: string;
|
|
130
|
+
/**
|
|
131
|
+
* AbortSignal to cancel the request
|
|
132
|
+
*/
|
|
133
|
+
signal?: AbortSignal;
|
|
23
134
|
}
|
|
24
135
|
/**
|
|
25
|
-
*
|
|
136
|
+
* Rate limit information from response headers
|
|
137
|
+
*/
|
|
138
|
+
interface RateLimitInfo {
|
|
139
|
+
/** Maximum requests allowed in the current window */
|
|
140
|
+
limit: number;
|
|
141
|
+
/** Remaining requests in the current window */
|
|
142
|
+
remaining: number;
|
|
143
|
+
/** Unix timestamp (seconds) when the rate limit resets */
|
|
144
|
+
reset: number;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Response wrapper with rate limit info
|
|
148
|
+
*/
|
|
149
|
+
interface ResponseWithRateLimit<T> {
|
|
150
|
+
data: T;
|
|
151
|
+
rateLimit?: RateLimitInfo;
|
|
152
|
+
requestId?: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* HTTP client with retry logic, hooks, and error handling
|
|
26
156
|
*/
|
|
27
157
|
declare class HttpClient {
|
|
28
158
|
private readonly config;
|
|
29
159
|
private readonly fetchFn;
|
|
160
|
+
private readonly retryConfig;
|
|
161
|
+
/** Rate limit info from the last response */
|
|
162
|
+
private _lastRateLimit?;
|
|
163
|
+
/** Request ID from the last response */
|
|
164
|
+
private _lastRequestId?;
|
|
30
165
|
constructor(config: HttpClientConfig);
|
|
166
|
+
/**
|
|
167
|
+
* Get rate limit info from the last response
|
|
168
|
+
*/
|
|
169
|
+
get lastRateLimit(): RateLimitInfo | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* Get request ID from the last response
|
|
172
|
+
*/
|
|
173
|
+
get lastRequestId(): string | undefined;
|
|
31
174
|
/**
|
|
32
175
|
* Execute an HTTP request with retry logic
|
|
33
176
|
*/
|
|
34
177
|
request<T>(options: RequestOptions): Promise<T>;
|
|
178
|
+
/**
|
|
179
|
+
* Execute request and return response with rate limit info
|
|
180
|
+
*/
|
|
181
|
+
requestWithRateLimit<T>(options: RequestOptions): Promise<ResponseWithRateLimit<T>>;
|
|
182
|
+
private shouldRetry;
|
|
183
|
+
private calculateDelay;
|
|
35
184
|
private buildUrl;
|
|
36
185
|
private buildHeaders;
|
|
37
186
|
private executeRequest;
|
|
187
|
+
private extractRateLimitInfo;
|
|
38
188
|
private handleResponse;
|
|
39
|
-
private calculateBackoff;
|
|
40
189
|
private sleep;
|
|
41
190
|
}
|
|
42
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Options for API requests
|
|
194
|
+
*/
|
|
195
|
+
interface ApiRequestOptions {
|
|
196
|
+
/**
|
|
197
|
+
* AbortSignal to cancel the request
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const controller = new AbortController();
|
|
202
|
+
* setTimeout(() => controller.abort(), 5000);
|
|
203
|
+
*
|
|
204
|
+
* const ticket = await client.tickets.get('ws_abc', 'tkt_xyz', {
|
|
205
|
+
* signal: controller.signal,
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
signal?: AbortSignal;
|
|
210
|
+
}
|
|
43
211
|
/**
|
|
44
212
|
* Base class for all resource classes
|
|
213
|
+
* @internal
|
|
45
214
|
*/
|
|
46
215
|
declare abstract class BaseResource {
|
|
47
216
|
protected readonly http: HttpClient;
|
|
48
217
|
constructor(http: HttpClient);
|
|
49
|
-
protected _get<T>(path: string, query?: RequestOptions['query']): Promise<T>;
|
|
218
|
+
protected _get<T>(path: string, query?: RequestOptions['query'], options?: ApiRequestOptions): Promise<T>;
|
|
50
219
|
protected _post<T>(path: string, body?: unknown, options?: {
|
|
51
220
|
idempotencyKey?: string;
|
|
52
221
|
query?: RequestOptions['query'];
|
|
222
|
+
signal?: AbortSignal;
|
|
53
223
|
}): Promise<T>;
|
|
54
|
-
protected _patch<T>(path: string, body?: unknown): Promise<T>;
|
|
55
|
-
protected _put<T>(path: string, body?: unknown): Promise<T>;
|
|
56
|
-
protected _delete<T>(path: string, query?: RequestOptions['query']): Promise<T>;
|
|
224
|
+
protected _patch<T>(path: string, body?: unknown, options?: ApiRequestOptions): Promise<T>;
|
|
225
|
+
protected _put<T>(path: string, body?: unknown, options?: ApiRequestOptions): Promise<T>;
|
|
226
|
+
protected _delete<T>(path: string, query?: RequestOptions['query'], options?: ApiRequestOptions): Promise<T>;
|
|
57
227
|
}
|
|
58
228
|
|
|
59
229
|
/**
|
|
@@ -217,6 +387,134 @@ interface DeleteBrandPreview {
|
|
|
217
387
|
tagCount: number;
|
|
218
388
|
}
|
|
219
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Portal API Types
|
|
392
|
+
*
|
|
393
|
+
* Types for the customer-facing portal API.
|
|
394
|
+
* Used with the DispatchPortal client.
|
|
395
|
+
*/
|
|
396
|
+
/**
|
|
397
|
+
* Response from portal token generation or verification
|
|
398
|
+
*/
|
|
399
|
+
interface PortalTokenResponse {
|
|
400
|
+
/** JWT portal token for Authorization header */
|
|
401
|
+
token: string;
|
|
402
|
+
/** ISO 8601 expiration timestamp */
|
|
403
|
+
expiresAt: string;
|
|
404
|
+
/** Customer ID */
|
|
405
|
+
customerId: string;
|
|
406
|
+
/** Customer email */
|
|
407
|
+
email: string;
|
|
408
|
+
/** Customer name (if provided) */
|
|
409
|
+
name?: string;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Input for generating a portal token
|
|
413
|
+
*/
|
|
414
|
+
interface GeneratePortalTokenInput {
|
|
415
|
+
/** Customer email address */
|
|
416
|
+
email: string;
|
|
417
|
+
/** Customer name (optional) */
|
|
418
|
+
name?: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Input for sending a magic link email
|
|
422
|
+
*/
|
|
423
|
+
interface SendMagicLinkInput {
|
|
424
|
+
/** Customer email address */
|
|
425
|
+
email: string;
|
|
426
|
+
/** URL to redirect to after verification (your portal URL) */
|
|
427
|
+
portalUrl: string;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Ticket summary (returned in list views)
|
|
431
|
+
*/
|
|
432
|
+
interface PortalTicket {
|
|
433
|
+
/** Ticket ID */
|
|
434
|
+
id: string;
|
|
435
|
+
/** Formatted ticket number (e.g., "TKT-1001") */
|
|
436
|
+
ticketNumber: string;
|
|
437
|
+
/** Ticket title */
|
|
438
|
+
title: string;
|
|
439
|
+
/** Current status */
|
|
440
|
+
status: string;
|
|
441
|
+
/** Priority level */
|
|
442
|
+
priority: string;
|
|
443
|
+
/** Number of comments on the ticket */
|
|
444
|
+
commentCount: number;
|
|
445
|
+
/** Creation timestamp */
|
|
446
|
+
createdAt: string;
|
|
447
|
+
/** Last update timestamp */
|
|
448
|
+
updatedAt: string;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Ticket detail with body and comments
|
|
452
|
+
*/
|
|
453
|
+
interface PortalTicketDetail extends PortalTicket {
|
|
454
|
+
/** Ticket body/description */
|
|
455
|
+
body: string;
|
|
456
|
+
/** Brand information */
|
|
457
|
+
brand: {
|
|
458
|
+
/** Brand name */
|
|
459
|
+
name: string;
|
|
460
|
+
};
|
|
461
|
+
/** Comments on the ticket (excludes internal comments) */
|
|
462
|
+
comments: PortalComment[];
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Comment on a ticket
|
|
466
|
+
*/
|
|
467
|
+
interface PortalComment {
|
|
468
|
+
/** Comment ID */
|
|
469
|
+
id: string;
|
|
470
|
+
/** Comment body */
|
|
471
|
+
body: string;
|
|
472
|
+
/** Author type */
|
|
473
|
+
authorType: 'CUSTOMER' | 'STAFF';
|
|
474
|
+
/** Author display name */
|
|
475
|
+
authorName: string;
|
|
476
|
+
/** Creation timestamp */
|
|
477
|
+
createdAt: string;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Input for creating a ticket via portal
|
|
481
|
+
*/
|
|
482
|
+
interface PortalCreateTicketInput {
|
|
483
|
+
/** Ticket title */
|
|
484
|
+
title: string;
|
|
485
|
+
/** Ticket body/description (optional) */
|
|
486
|
+
body?: string;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Filters for listing tickets
|
|
490
|
+
*/
|
|
491
|
+
interface PortalListTicketsFilters {
|
|
492
|
+
/** Filter by status */
|
|
493
|
+
status?: string;
|
|
494
|
+
/** Sort field */
|
|
495
|
+
sort?: 'createdAt' | 'updatedAt';
|
|
496
|
+
/** Sort order */
|
|
497
|
+
order?: 'asc' | 'desc';
|
|
498
|
+
/** Maximum results per page (1-50, default 20) */
|
|
499
|
+
limit?: number;
|
|
500
|
+
/** Pagination cursor */
|
|
501
|
+
cursor?: string;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Paginated list of tickets
|
|
505
|
+
*/
|
|
506
|
+
interface PortalTicketListResponse {
|
|
507
|
+
/** Array of tickets */
|
|
508
|
+
data: PortalTicket[];
|
|
509
|
+
/** Pagination info */
|
|
510
|
+
pagination: {
|
|
511
|
+
/** Whether more results are available */
|
|
512
|
+
hasMore: boolean;
|
|
513
|
+
/** Cursor for next page (null if no more) */
|
|
514
|
+
nextCursor: string | null;
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
220
518
|
/**
|
|
221
519
|
* Brands resource for managing workspaces
|
|
222
520
|
*/
|
|
@@ -271,6 +569,61 @@ declare class BrandsResource extends BaseResource {
|
|
|
271
569
|
* ```
|
|
272
570
|
*/
|
|
273
571
|
getInboundEmail(brandId: string, domain?: string): string;
|
|
572
|
+
/**
|
|
573
|
+
* Generate a portal token for a customer
|
|
574
|
+
*
|
|
575
|
+
* Use this for "authenticated mode" where your backend already knows who the
|
|
576
|
+
* customer is (they're logged into your app). Generate a portal token and
|
|
577
|
+
* pass it to your frontend to initialize the DispatchPortal client.
|
|
578
|
+
*
|
|
579
|
+
* @param brandId - The brand ID
|
|
580
|
+
* @param data - Customer email and optional name
|
|
581
|
+
* @returns Portal token response with JWT token and expiry
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* // On your backend (Node.js, Next.js API route, etc.)
|
|
586
|
+
* const { token, expiresAt } = await client.brands.generatePortalToken(
|
|
587
|
+
* 'br_abc123',
|
|
588
|
+
* {
|
|
589
|
+
* email: req.user.email,
|
|
590
|
+
* name: req.user.name,
|
|
591
|
+
* }
|
|
592
|
+
* );
|
|
593
|
+
*
|
|
594
|
+
* // Return token to your frontend
|
|
595
|
+
* res.json({ portalToken: token });
|
|
596
|
+
* ```
|
|
597
|
+
*/
|
|
598
|
+
generatePortalToken(brandId: string, data: GeneratePortalTokenInput): Promise<PortalTokenResponse>;
|
|
599
|
+
/**
|
|
600
|
+
* Send a magic link email to a customer
|
|
601
|
+
*
|
|
602
|
+
* Use this for "self-auth mode" where customers access the portal without
|
|
603
|
+
* being logged into your app. They receive an email with a link that
|
|
604
|
+
* authenticates them directly.
|
|
605
|
+
*
|
|
606
|
+
* @param brandId - The brand ID
|
|
607
|
+
* @param data - Customer email and portal URL to redirect to
|
|
608
|
+
* @returns Success status
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* // Customer requests access via your portal login form
|
|
613
|
+
* await client.brands.sendPortalMagicLink('br_abc123', {
|
|
614
|
+
* email: 'customer@example.com',
|
|
615
|
+
* portalUrl: 'https://yourapp.com/support/portal',
|
|
616
|
+
* });
|
|
617
|
+
*
|
|
618
|
+
* // Customer receives email with link like:
|
|
619
|
+
* // https://yourapp.com/support/portal?token=xyz...
|
|
620
|
+
*
|
|
621
|
+
* // Your portal page then calls DispatchPortal.verify(token)
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
sendPortalMagicLink(brandId: string, data: SendMagicLinkInput): Promise<{
|
|
625
|
+
success: boolean;
|
|
626
|
+
}>;
|
|
274
627
|
}
|
|
275
628
|
|
|
276
629
|
/**
|
|
@@ -1024,10 +1377,30 @@ declare class FieldsResource extends BaseResource {
|
|
|
1024
1377
|
|
|
1025
1378
|
/**
|
|
1026
1379
|
* Configuration options for the Dispatch Tickets client
|
|
1380
|
+
*
|
|
1381
|
+
* @example
|
|
1382
|
+
* ```typescript
|
|
1383
|
+
* const client = new DispatchTickets({
|
|
1384
|
+
* apiKey: 'sk_live_...',
|
|
1385
|
+
* timeout: 30000,
|
|
1386
|
+
* retry: {
|
|
1387
|
+
* maxRetries: 5,
|
|
1388
|
+
* retryableStatuses: [429, 500, 502, 503, 504],
|
|
1389
|
+
* },
|
|
1390
|
+
* hooks: {
|
|
1391
|
+
* onRequest: (ctx) => console.log(`${ctx.method} ${ctx.url}`),
|
|
1392
|
+
* onResponse: (ctx) => console.log(`${ctx.status} in ${ctx.durationMs}ms`),
|
|
1393
|
+
* },
|
|
1394
|
+
* });
|
|
1395
|
+
* ```
|
|
1027
1396
|
*/
|
|
1028
1397
|
interface DispatchTicketsConfig {
|
|
1029
1398
|
/**
|
|
1030
1399
|
* Your API key (required)
|
|
1400
|
+
*
|
|
1401
|
+
* Get your API key from the Dispatch Tickets dashboard.
|
|
1402
|
+
* Keys starting with `sk_live_` are production keys.
|
|
1403
|
+
* Keys starting with `sk_test_` are test keys.
|
|
1031
1404
|
*/
|
|
1032
1405
|
apiKey: string;
|
|
1033
1406
|
/**
|
|
@@ -1043,22 +1416,90 @@ interface DispatchTicketsConfig {
|
|
|
1043
1416
|
/**
|
|
1044
1417
|
* Maximum number of retries for failed requests
|
|
1045
1418
|
* @default 3
|
|
1419
|
+
* @deprecated Use `retry.maxRetries` instead for fine-grained control
|
|
1046
1420
|
*/
|
|
1047
1421
|
maxRetries?: number;
|
|
1048
1422
|
/**
|
|
1049
1423
|
* Enable debug logging
|
|
1424
|
+
*
|
|
1425
|
+
* When enabled, logs all requests, responses, and request IDs to console.
|
|
1050
1426
|
* @default false
|
|
1051
1427
|
*/
|
|
1052
1428
|
debug?: boolean;
|
|
1053
1429
|
/**
|
|
1054
1430
|
* Custom fetch implementation for testing/mocking
|
|
1431
|
+
*
|
|
1432
|
+
* @example
|
|
1433
|
+
* ```typescript
|
|
1434
|
+
* const mockFetch = vi.fn().mockResolvedValue({
|
|
1435
|
+
* ok: true,
|
|
1436
|
+
* status: 200,
|
|
1437
|
+
* headers: { get: () => 'application/json' },
|
|
1438
|
+
* json: () => Promise.resolve({ id: 'test' }),
|
|
1439
|
+
* });
|
|
1440
|
+
*
|
|
1441
|
+
* const client = new DispatchTickets({
|
|
1442
|
+
* apiKey: 'sk_test_...',
|
|
1443
|
+
* fetch: mockFetch,
|
|
1444
|
+
* });
|
|
1445
|
+
* ```
|
|
1055
1446
|
*/
|
|
1056
1447
|
fetch?: FetchFunction;
|
|
1448
|
+
/**
|
|
1449
|
+
* Fine-grained retry configuration
|
|
1450
|
+
*
|
|
1451
|
+
* @example
|
|
1452
|
+
* ```typescript
|
|
1453
|
+
* const client = new DispatchTickets({
|
|
1454
|
+
* apiKey: 'sk_live_...',
|
|
1455
|
+
* retry: {
|
|
1456
|
+
* maxRetries: 5,
|
|
1457
|
+
* retryableStatuses: [429, 500, 502, 503, 504],
|
|
1458
|
+
* initialDelayMs: 500,
|
|
1459
|
+
* maxDelayMs: 60000,
|
|
1460
|
+
* backoffMultiplier: 2,
|
|
1461
|
+
* jitter: 0.25,
|
|
1462
|
+
* },
|
|
1463
|
+
* });
|
|
1464
|
+
* ```
|
|
1465
|
+
*/
|
|
1466
|
+
retry?: RetryConfig;
|
|
1467
|
+
/**
|
|
1468
|
+
* Hooks for observability and customization
|
|
1469
|
+
*
|
|
1470
|
+
* @example
|
|
1471
|
+
* ```typescript
|
|
1472
|
+
* const client = new DispatchTickets({
|
|
1473
|
+
* apiKey: 'sk_live_...',
|
|
1474
|
+
* hooks: {
|
|
1475
|
+
* onRequest: (ctx) => {
|
|
1476
|
+
* console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.url}`);
|
|
1477
|
+
* },
|
|
1478
|
+
* onResponse: (ctx) => {
|
|
1479
|
+
* console.log(`[${ctx.requestId}] ${ctx.status} in ${ctx.durationMs}ms`);
|
|
1480
|
+
* },
|
|
1481
|
+
* onError: (error, ctx) => {
|
|
1482
|
+
* console.error(`Request failed: ${error.message}`);
|
|
1483
|
+
* // Send to error tracking service
|
|
1484
|
+
* Sentry.captureException(error);
|
|
1485
|
+
* },
|
|
1486
|
+
* onRetry: (ctx, error, delayMs) => {
|
|
1487
|
+
* console.log(`Retrying in ${delayMs}ms (attempt ${ctx.attempt + 1})`);
|
|
1488
|
+
* },
|
|
1489
|
+
* },
|
|
1490
|
+
* });
|
|
1491
|
+
* ```
|
|
1492
|
+
*/
|
|
1493
|
+
hooks?: Hooks;
|
|
1057
1494
|
}
|
|
1058
1495
|
/**
|
|
1059
1496
|
* Dispatch Tickets SDK client
|
|
1060
1497
|
*
|
|
1061
|
-
*
|
|
1498
|
+
* The main entry point for interacting with the Dispatch Tickets API.
|
|
1499
|
+
* Create a client instance with your API key and use the resource methods
|
|
1500
|
+
* to manage tickets, comments, attachments, and more.
|
|
1501
|
+
*
|
|
1502
|
+
* @example Basic usage
|
|
1062
1503
|
* ```typescript
|
|
1063
1504
|
* import { DispatchTickets } from '@dispatchtickets/sdk';
|
|
1064
1505
|
*
|
|
@@ -1080,59 +1521,530 @@ interface DispatchTicketsConfig {
|
|
|
1080
1521
|
* console.log(ticket.title);
|
|
1081
1522
|
* }
|
|
1082
1523
|
* ```
|
|
1524
|
+
*
|
|
1525
|
+
* @example With request cancellation
|
|
1526
|
+
* ```typescript
|
|
1527
|
+
* const controller = new AbortController();
|
|
1528
|
+
*
|
|
1529
|
+
* // Cancel after 5 seconds
|
|
1530
|
+
* setTimeout(() => controller.abort(), 5000);
|
|
1531
|
+
*
|
|
1532
|
+
* try {
|
|
1533
|
+
* const tickets = await client.tickets.listPage('ws_abc123', {}, {
|
|
1534
|
+
* signal: controller.signal,
|
|
1535
|
+
* });
|
|
1536
|
+
* } catch (error) {
|
|
1537
|
+
* if (error.message === 'Request aborted by user') {
|
|
1538
|
+
* console.log('Request was cancelled');
|
|
1539
|
+
* }
|
|
1540
|
+
* }
|
|
1541
|
+
* ```
|
|
1542
|
+
*
|
|
1543
|
+
* @example With hooks for logging
|
|
1544
|
+
* ```typescript
|
|
1545
|
+
* const client = new DispatchTickets({
|
|
1546
|
+
* apiKey: 'sk_live_...',
|
|
1547
|
+
* hooks: {
|
|
1548
|
+
* onRequest: (ctx) => console.log(`→ ${ctx.method} ${ctx.url}`),
|
|
1549
|
+
* onResponse: (ctx) => console.log(`← ${ctx.status} (${ctx.durationMs}ms)`),
|
|
1550
|
+
* },
|
|
1551
|
+
* });
|
|
1552
|
+
* ```
|
|
1083
1553
|
*/
|
|
1084
1554
|
declare class DispatchTickets {
|
|
1085
1555
|
private readonly http;
|
|
1086
1556
|
/**
|
|
1087
1557
|
* Accounts resource for managing the current account and API keys
|
|
1558
|
+
*
|
|
1559
|
+
* @example
|
|
1560
|
+
* ```typescript
|
|
1561
|
+
* // Get current account
|
|
1562
|
+
* const account = await client.accounts.me();
|
|
1563
|
+
*
|
|
1564
|
+
* // Get usage statistics
|
|
1565
|
+
* const usage = await client.accounts.getUsage();
|
|
1566
|
+
*
|
|
1567
|
+
* // Create a new API key
|
|
1568
|
+
* const newKey = await client.accounts.createApiKey({
|
|
1569
|
+
* name: 'Production',
|
|
1570
|
+
* allBrands: true,
|
|
1571
|
+
* });
|
|
1572
|
+
* ```
|
|
1088
1573
|
*/
|
|
1089
1574
|
readonly accounts: AccountsResource;
|
|
1090
1575
|
/**
|
|
1091
1576
|
* Brands (workspaces) resource
|
|
1577
|
+
*
|
|
1578
|
+
* Brands are isolated containers for tickets. Each brand can have its own
|
|
1579
|
+
* email address, categories, tags, and settings.
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* // List all brands
|
|
1584
|
+
* const brands = await client.brands.list();
|
|
1585
|
+
*
|
|
1586
|
+
* // Create a new brand
|
|
1587
|
+
* const brand = await client.brands.create({
|
|
1588
|
+
* name: 'Acme Support',
|
|
1589
|
+
* slug: 'acme',
|
|
1590
|
+
* });
|
|
1591
|
+
*
|
|
1592
|
+
* // Get inbound email address
|
|
1593
|
+
* const email = client.brands.getInboundEmail('br_abc123');
|
|
1594
|
+
* // Returns: br_abc123@inbound.dispatchtickets.com
|
|
1595
|
+
* ```
|
|
1092
1596
|
*/
|
|
1093
1597
|
readonly brands: BrandsResource;
|
|
1094
1598
|
/**
|
|
1095
1599
|
* Tickets resource
|
|
1600
|
+
*
|
|
1601
|
+
* @example
|
|
1602
|
+
* ```typescript
|
|
1603
|
+
* // Create a ticket
|
|
1604
|
+
* const ticket = await client.tickets.create('ws_abc123', {
|
|
1605
|
+
* title: 'Issue with billing',
|
|
1606
|
+
* body: 'I was charged twice...',
|
|
1607
|
+
* priority: 'high',
|
|
1608
|
+
* });
|
|
1609
|
+
*
|
|
1610
|
+
* // Iterate through all tickets
|
|
1611
|
+
* for await (const ticket of client.tickets.list('ws_abc123', { status: 'open' })) {
|
|
1612
|
+
* console.log(ticket.title);
|
|
1613
|
+
* }
|
|
1614
|
+
* ```
|
|
1096
1615
|
*/
|
|
1097
1616
|
readonly tickets: TicketsResource;
|
|
1098
1617
|
/**
|
|
1099
1618
|
* Comments resource
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```typescript
|
|
1622
|
+
* // Add a comment
|
|
1623
|
+
* const comment = await client.comments.create('ws_abc123', 'tkt_xyz', {
|
|
1624
|
+
* body: 'Thanks for your patience!',
|
|
1625
|
+
* authorType: 'AGENT',
|
|
1626
|
+
* });
|
|
1627
|
+
*
|
|
1628
|
+
* // List comments
|
|
1629
|
+
* const comments = await client.comments.list('ws_abc123', 'tkt_xyz');
|
|
1630
|
+
* ```
|
|
1100
1631
|
*/
|
|
1101
1632
|
readonly comments: CommentsResource;
|
|
1102
1633
|
/**
|
|
1103
1634
|
* Attachments resource
|
|
1635
|
+
*
|
|
1636
|
+
* @example
|
|
1637
|
+
* ```typescript
|
|
1638
|
+
* // Simple upload
|
|
1639
|
+
* const attachment = await client.attachments.upload(
|
|
1640
|
+
* 'ws_abc123',
|
|
1641
|
+
* 'tkt_xyz',
|
|
1642
|
+
* fileBuffer,
|
|
1643
|
+
* 'document.pdf',
|
|
1644
|
+
* 'application/pdf'
|
|
1645
|
+
* );
|
|
1646
|
+
*
|
|
1647
|
+
* // Get download URL
|
|
1648
|
+
* const { downloadUrl } = await client.attachments.get('ws_abc123', 'tkt_xyz', 'att_abc');
|
|
1649
|
+
* ```
|
|
1104
1650
|
*/
|
|
1105
1651
|
readonly attachments: AttachmentsResource;
|
|
1106
1652
|
/**
|
|
1107
1653
|
* Webhooks resource
|
|
1654
|
+
*
|
|
1655
|
+
* @example
|
|
1656
|
+
* ```typescript
|
|
1657
|
+
* // Create a webhook
|
|
1658
|
+
* const webhook = await client.webhooks.create('ws_abc123', {
|
|
1659
|
+
* url: 'https://example.com/webhook',
|
|
1660
|
+
* secret: 'your-secret',
|
|
1661
|
+
* events: ['ticket.created', 'ticket.updated'],
|
|
1662
|
+
* });
|
|
1663
|
+
* ```
|
|
1108
1664
|
*/
|
|
1109
1665
|
readonly webhooks: WebhooksResource;
|
|
1110
1666
|
/**
|
|
1111
1667
|
* Categories resource
|
|
1668
|
+
*
|
|
1669
|
+
* @example
|
|
1670
|
+
* ```typescript
|
|
1671
|
+
* // Create a category
|
|
1672
|
+
* await client.categories.create('ws_abc123', { name: 'Billing', color: '#ef4444' });
|
|
1673
|
+
*
|
|
1674
|
+
* // Get category stats
|
|
1675
|
+
* const stats = await client.categories.getStats('ws_abc123');
|
|
1676
|
+
* ```
|
|
1112
1677
|
*/
|
|
1113
1678
|
readonly categories: CategoriesResource;
|
|
1114
1679
|
/**
|
|
1115
1680
|
* Tags resource
|
|
1681
|
+
*
|
|
1682
|
+
* @example
|
|
1683
|
+
* ```typescript
|
|
1684
|
+
* // Create a tag
|
|
1685
|
+
* await client.tags.create('ws_abc123', { name: 'urgent', color: '#f59e0b' });
|
|
1686
|
+
*
|
|
1687
|
+
* // Merge tags
|
|
1688
|
+
* await client.tags.merge('ws_abc123', 'tag_target', ['tag_source1', 'tag_source2']);
|
|
1689
|
+
* ```
|
|
1116
1690
|
*/
|
|
1117
1691
|
readonly tags: TagsResource;
|
|
1118
1692
|
/**
|
|
1119
1693
|
* Customers resource
|
|
1694
|
+
*
|
|
1695
|
+
* @example
|
|
1696
|
+
* ```typescript
|
|
1697
|
+
* // Create a customer
|
|
1698
|
+
* const customer = await client.customers.create('ws_abc123', {
|
|
1699
|
+
* email: 'user@example.com',
|
|
1700
|
+
* name: 'Jane Doe',
|
|
1701
|
+
* });
|
|
1702
|
+
*
|
|
1703
|
+
* // Search customers
|
|
1704
|
+
* const results = await client.customers.search('ws_abc123', 'jane');
|
|
1705
|
+
* ```
|
|
1120
1706
|
*/
|
|
1121
1707
|
readonly customers: CustomersResource;
|
|
1122
1708
|
/**
|
|
1123
1709
|
* Custom fields resource
|
|
1710
|
+
*
|
|
1711
|
+
* @example
|
|
1712
|
+
* ```typescript
|
|
1713
|
+
* // Get all field definitions
|
|
1714
|
+
* const fields = await client.fields.getAll('ws_abc123');
|
|
1715
|
+
*
|
|
1716
|
+
* // Create a field
|
|
1717
|
+
* await client.fields.create('ws_abc123', 'ticket', {
|
|
1718
|
+
* key: 'order_id',
|
|
1719
|
+
* label: 'Order ID',
|
|
1720
|
+
* type: 'text',
|
|
1721
|
+
* required: true,
|
|
1722
|
+
* });
|
|
1723
|
+
* ```
|
|
1124
1724
|
*/
|
|
1125
1725
|
readonly fields: FieldsResource;
|
|
1126
1726
|
/**
|
|
1127
1727
|
* Static webhook utilities
|
|
1728
|
+
*
|
|
1729
|
+
* @example
|
|
1730
|
+
* ```typescript
|
|
1731
|
+
* // Verify webhook signature
|
|
1732
|
+
* const isValid = DispatchTickets.webhooks.verifySignature(
|
|
1733
|
+
* rawBody,
|
|
1734
|
+
* req.headers['x-dispatch-signature'],
|
|
1735
|
+
* 'your-secret'
|
|
1736
|
+
* );
|
|
1737
|
+
*
|
|
1738
|
+
* // Generate signature for testing
|
|
1739
|
+
* const signature = DispatchTickets.webhooks.generateSignature(
|
|
1740
|
+
* JSON.stringify(payload),
|
|
1741
|
+
* 'your-secret'
|
|
1742
|
+
* );
|
|
1743
|
+
* ```
|
|
1128
1744
|
*/
|
|
1129
1745
|
static readonly webhooks: {
|
|
1130
1746
|
verifySignature(payload: string, signature: string, secret: string): boolean;
|
|
1131
1747
|
generateSignature(payload: string, secret: string): string;
|
|
1132
1748
|
};
|
|
1749
|
+
/**
|
|
1750
|
+
* Create a new Dispatch Tickets client
|
|
1751
|
+
*
|
|
1752
|
+
* @param config - Client configuration options
|
|
1753
|
+
* @throws Error if API key is not provided
|
|
1754
|
+
*
|
|
1755
|
+
* @example
|
|
1756
|
+
* ```typescript
|
|
1757
|
+
* const client = new DispatchTickets({
|
|
1758
|
+
* apiKey: 'sk_live_...',
|
|
1759
|
+
* });
|
|
1760
|
+
* ```
|
|
1761
|
+
*/
|
|
1133
1762
|
constructor(config: DispatchTicketsConfig);
|
|
1134
1763
|
}
|
|
1135
1764
|
|
|
1765
|
+
/**
|
|
1766
|
+
* Options for creating a ticket via portal
|
|
1767
|
+
*/
|
|
1768
|
+
interface PortalCreateTicketOptions extends ApiRequestOptions {
|
|
1769
|
+
/** Idempotency key to prevent duplicate creation */
|
|
1770
|
+
idempotencyKey?: string;
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Options for adding a comment via portal
|
|
1774
|
+
*/
|
|
1775
|
+
interface PortalAddCommentOptions extends ApiRequestOptions {
|
|
1776
|
+
/** Idempotency key to prevent duplicate creation */
|
|
1777
|
+
idempotencyKey?: string;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Portal tickets resource
|
|
1781
|
+
*
|
|
1782
|
+
* Provides methods for customers to manage their tickets via the portal API.
|
|
1783
|
+
*
|
|
1784
|
+
* @example
|
|
1785
|
+
* ```typescript
|
|
1786
|
+
* const portal = new DispatchPortal({ token: 'portal_token...' });
|
|
1787
|
+
*
|
|
1788
|
+
* // List tickets
|
|
1789
|
+
* const { data: tickets } = await portal.tickets.list();
|
|
1790
|
+
*
|
|
1791
|
+
* // Create a ticket
|
|
1792
|
+
* const ticket = await portal.tickets.create({
|
|
1793
|
+
* title: 'Need help with my order',
|
|
1794
|
+
* body: 'Order #12345 has not arrived...',
|
|
1795
|
+
* });
|
|
1796
|
+
*
|
|
1797
|
+
* // Add a comment
|
|
1798
|
+
* await portal.tickets.addComment(ticket.id, 'Any update on this?');
|
|
1799
|
+
* ```
|
|
1800
|
+
*/
|
|
1801
|
+
declare class PortalTicketsResource extends BaseResource {
|
|
1802
|
+
/**
|
|
1803
|
+
* List the customer's tickets
|
|
1804
|
+
*
|
|
1805
|
+
* Returns a paginated list of tickets belonging to the authenticated customer.
|
|
1806
|
+
*
|
|
1807
|
+
* @param filters - Optional filters and pagination
|
|
1808
|
+
* @param options - Request options
|
|
1809
|
+
* @returns Paginated ticket list
|
|
1810
|
+
*
|
|
1811
|
+
* @example
|
|
1812
|
+
* ```typescript
|
|
1813
|
+
* // List all tickets
|
|
1814
|
+
* const { data: tickets, pagination } = await portal.tickets.list();
|
|
1815
|
+
*
|
|
1816
|
+
* // Filter by status
|
|
1817
|
+
* const openTickets = await portal.tickets.list({ status: 'open' });
|
|
1818
|
+
*
|
|
1819
|
+
* // Paginate
|
|
1820
|
+
* const page2 = await portal.tickets.list({ cursor: pagination.nextCursor });
|
|
1821
|
+
* ```
|
|
1822
|
+
*/
|
|
1823
|
+
list(filters?: PortalListTicketsFilters, options?: ApiRequestOptions): Promise<PortalTicketListResponse>;
|
|
1824
|
+
/**
|
|
1825
|
+
* Iterate through all tickets with automatic pagination
|
|
1826
|
+
*
|
|
1827
|
+
* @param filters - Optional filters (cursor is managed automatically)
|
|
1828
|
+
* @returns Async iterator of tickets
|
|
1829
|
+
*
|
|
1830
|
+
* @example
|
|
1831
|
+
* ```typescript
|
|
1832
|
+
* for await (const ticket of portal.tickets.listAll({ status: 'open' })) {
|
|
1833
|
+
* console.log(ticket.title);
|
|
1834
|
+
* }
|
|
1835
|
+
* ```
|
|
1836
|
+
*/
|
|
1837
|
+
listAll(filters?: Omit<PortalListTicketsFilters, 'cursor'>): AsyncIterable<PortalTicket>;
|
|
1838
|
+
/**
|
|
1839
|
+
* Get a ticket by ID
|
|
1840
|
+
*
|
|
1841
|
+
* Returns the ticket with its comments (excludes internal comments).
|
|
1842
|
+
*
|
|
1843
|
+
* @param ticketId - Ticket ID
|
|
1844
|
+
* @param options - Request options
|
|
1845
|
+
* @returns Ticket detail with comments
|
|
1846
|
+
*
|
|
1847
|
+
* @example
|
|
1848
|
+
* ```typescript
|
|
1849
|
+
* const ticket = await portal.tickets.get('tkt_abc123');
|
|
1850
|
+
* console.log(ticket.title);
|
|
1851
|
+
* console.log(`${ticket.comments.length} comments`);
|
|
1852
|
+
* ```
|
|
1853
|
+
*/
|
|
1854
|
+
get(ticketId: string, options?: ApiRequestOptions): Promise<PortalTicketDetail>;
|
|
1855
|
+
/**
|
|
1856
|
+
* Create a new ticket
|
|
1857
|
+
*
|
|
1858
|
+
* @param data - Ticket data
|
|
1859
|
+
* @param options - Request options including idempotency key
|
|
1860
|
+
* @returns Created ticket
|
|
1861
|
+
*
|
|
1862
|
+
* @example
|
|
1863
|
+
* ```typescript
|
|
1864
|
+
* const ticket = await portal.tickets.create({
|
|
1865
|
+
* title: 'Need help with billing',
|
|
1866
|
+
* body: 'I was charged twice for my subscription...',
|
|
1867
|
+
* });
|
|
1868
|
+
* ```
|
|
1869
|
+
*/
|
|
1870
|
+
create(data: PortalCreateTicketInput, options?: PortalCreateTicketOptions): Promise<PortalTicket>;
|
|
1871
|
+
/**
|
|
1872
|
+
* Add a comment to a ticket
|
|
1873
|
+
*
|
|
1874
|
+
* @param ticketId - Ticket ID
|
|
1875
|
+
* @param body - Comment text
|
|
1876
|
+
* @param options - Request options including idempotency key
|
|
1877
|
+
* @returns Created comment
|
|
1878
|
+
*
|
|
1879
|
+
* @example
|
|
1880
|
+
* ```typescript
|
|
1881
|
+
* const comment = await portal.tickets.addComment(
|
|
1882
|
+
* 'tkt_abc123',
|
|
1883
|
+
* 'Here is the additional information you requested...'
|
|
1884
|
+
* );
|
|
1885
|
+
* ```
|
|
1886
|
+
*/
|
|
1887
|
+
addComment(ticketId: string, body: string, options?: PortalAddCommentOptions): Promise<PortalComment>;
|
|
1888
|
+
/**
|
|
1889
|
+
* Build query parameters from filters
|
|
1890
|
+
*/
|
|
1891
|
+
private buildListQuery;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Configuration options for the Dispatch Portal client
|
|
1896
|
+
*
|
|
1897
|
+
* @example
|
|
1898
|
+
* ```typescript
|
|
1899
|
+
* const portal = new DispatchPortal({
|
|
1900
|
+
* token: 'portal_token_from_backend...',
|
|
1901
|
+
* });
|
|
1902
|
+
* ```
|
|
1903
|
+
*/
|
|
1904
|
+
interface DispatchPortalConfig {
|
|
1905
|
+
/**
|
|
1906
|
+
* Portal token (required)
|
|
1907
|
+
*
|
|
1908
|
+
* Obtain this token by:
|
|
1909
|
+
* 1. Your backend calls `client.brands.generatePortalToken()` with the customer's email
|
|
1910
|
+
* 2. Pass the token to your frontend
|
|
1911
|
+
* 3. Frontend creates DispatchPortal with this token
|
|
1912
|
+
*
|
|
1913
|
+
* Or for self-auth mode:
|
|
1914
|
+
* 1. Customer clicks magic link email
|
|
1915
|
+
* 2. Your portal page calls `DispatchPortal.verify()` with the URL token
|
|
1916
|
+
* 3. Use the returned token to create DispatchPortal
|
|
1917
|
+
*/
|
|
1918
|
+
token: string;
|
|
1919
|
+
/**
|
|
1920
|
+
* Base URL for the API
|
|
1921
|
+
* @default 'https://dispatch-tickets-api.onrender.com/v1'
|
|
1922
|
+
*/
|
|
1923
|
+
baseUrl?: string;
|
|
1924
|
+
/**
|
|
1925
|
+
* Request timeout in milliseconds
|
|
1926
|
+
* @default 30000
|
|
1927
|
+
*/
|
|
1928
|
+
timeout?: number;
|
|
1929
|
+
/**
|
|
1930
|
+
* Custom fetch implementation (for testing)
|
|
1931
|
+
*/
|
|
1932
|
+
fetch?: FetchFunction;
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Dispatch Portal SDK client
|
|
1936
|
+
*
|
|
1937
|
+
* Customer-facing client for interacting with the portal API.
|
|
1938
|
+
* Use this to let customers view and manage their own tickets.
|
|
1939
|
+
*
|
|
1940
|
+
* @example Authenticated mode (backend generates token)
|
|
1941
|
+
* ```typescript
|
|
1942
|
+
* // On your backend:
|
|
1943
|
+
* const client = new DispatchTickets({ apiKey: 'sk_live_...' });
|
|
1944
|
+
* const { token } = await client.brands.generatePortalToken('br_abc123', {
|
|
1945
|
+
* email: 'customer@example.com',
|
|
1946
|
+
* name: 'Jane Doe',
|
|
1947
|
+
* });
|
|
1948
|
+
* // Pass token to frontend...
|
|
1949
|
+
*
|
|
1950
|
+
* // On your frontend:
|
|
1951
|
+
* const portal = new DispatchPortal({ token });
|
|
1952
|
+
* const { data: tickets } = await portal.tickets.list();
|
|
1953
|
+
* ```
|
|
1954
|
+
*
|
|
1955
|
+
* @example Self-auth mode (magic link)
|
|
1956
|
+
* ```typescript
|
|
1957
|
+
* // Customer clicks magic link, lands on your portal with ?token=xyz
|
|
1958
|
+
* const urlToken = new URLSearchParams(window.location.search).get('token');
|
|
1959
|
+
*
|
|
1960
|
+
* // Verify the magic link token
|
|
1961
|
+
* const { token } = await DispatchPortal.verify(urlToken);
|
|
1962
|
+
*
|
|
1963
|
+
* // Now use the portal token
|
|
1964
|
+
* const portal = new DispatchPortal({ token });
|
|
1965
|
+
* const { data: tickets } = await portal.tickets.list();
|
|
1966
|
+
* ```
|
|
1967
|
+
*/
|
|
1968
|
+
declare class DispatchPortal {
|
|
1969
|
+
private readonly http;
|
|
1970
|
+
private readonly config;
|
|
1971
|
+
/**
|
|
1972
|
+
* Tickets resource for viewing and creating tickets
|
|
1973
|
+
*
|
|
1974
|
+
* @example
|
|
1975
|
+
* ```typescript
|
|
1976
|
+
* // List tickets
|
|
1977
|
+
* const { data: tickets } = await portal.tickets.list();
|
|
1978
|
+
*
|
|
1979
|
+
* // Create a ticket
|
|
1980
|
+
* const ticket = await portal.tickets.create({
|
|
1981
|
+
* title: 'Need help',
|
|
1982
|
+
* body: 'Something is broken...',
|
|
1983
|
+
* });
|
|
1984
|
+
*
|
|
1985
|
+
* // Add a comment
|
|
1986
|
+
* await portal.tickets.addComment(ticket.id, 'Here is more info...');
|
|
1987
|
+
* ```
|
|
1988
|
+
*/
|
|
1989
|
+
readonly tickets: PortalTicketsResource;
|
|
1990
|
+
/**
|
|
1991
|
+
* Create a new Dispatch Portal client
|
|
1992
|
+
*
|
|
1993
|
+
* @param config - Portal client configuration
|
|
1994
|
+
* @throws Error if token is not provided
|
|
1995
|
+
*/
|
|
1996
|
+
constructor(config: DispatchPortalConfig);
|
|
1997
|
+
/**
|
|
1998
|
+
* Verify a magic link token and get a portal token
|
|
1999
|
+
*
|
|
2000
|
+
* Call this when the customer clicks the magic link and lands on your portal.
|
|
2001
|
+
* The magic link contains a short-lived token that can be exchanged for a
|
|
2002
|
+
* longer-lived portal token.
|
|
2003
|
+
*
|
|
2004
|
+
* @param magicLinkToken - The token from the magic link URL
|
|
2005
|
+
* @param baseUrl - Optional API base URL
|
|
2006
|
+
* @param fetchFn - Optional custom fetch function (for testing)
|
|
2007
|
+
* @returns Portal token response
|
|
2008
|
+
*
|
|
2009
|
+
* @example
|
|
2010
|
+
* ```typescript
|
|
2011
|
+
* // Customer lands on: https://yourapp.com/portal?token=xyz
|
|
2012
|
+
* const urlToken = new URLSearchParams(window.location.search).get('token');
|
|
2013
|
+
*
|
|
2014
|
+
* const { token, email, name } = await DispatchPortal.verify(urlToken);
|
|
2015
|
+
*
|
|
2016
|
+
* // Store token and create client
|
|
2017
|
+
* localStorage.setItem('portalToken', token);
|
|
2018
|
+
* const portal = new DispatchPortal({ token });
|
|
2019
|
+
* ```
|
|
2020
|
+
*/
|
|
2021
|
+
static verify(magicLinkToken: string, baseUrl?: string, fetchFn?: FetchFunction): Promise<PortalTokenResponse>;
|
|
2022
|
+
/**
|
|
2023
|
+
* Refresh the current portal token
|
|
2024
|
+
*
|
|
2025
|
+
* Call this before the token expires to get a new token with extended expiry.
|
|
2026
|
+
* The new token will have the same customer context.
|
|
2027
|
+
*
|
|
2028
|
+
* @returns New portal token response
|
|
2029
|
+
*
|
|
2030
|
+
* @example
|
|
2031
|
+
* ```typescript
|
|
2032
|
+
* // Check if token is close to expiry and refresh
|
|
2033
|
+
* const { token: newToken, expiresAt } = await portal.refresh();
|
|
2034
|
+
*
|
|
2035
|
+
* // Create new client with refreshed token
|
|
2036
|
+
* const newPortal = new DispatchPortal({ token: newToken });
|
|
2037
|
+
* ```
|
|
2038
|
+
*/
|
|
2039
|
+
refresh(): Promise<PortalTokenResponse>;
|
|
2040
|
+
/**
|
|
2041
|
+
* Get the current token
|
|
2042
|
+
*
|
|
2043
|
+
* Useful for storing/passing the token.
|
|
2044
|
+
*/
|
|
2045
|
+
get token(): string;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
1136
2048
|
/**
|
|
1137
2049
|
* Base error class for all Dispatch Tickets SDK errors
|
|
1138
2050
|
*/
|
|
@@ -1140,20 +2052,32 @@ declare class DispatchTicketsError extends Error {
|
|
|
1140
2052
|
readonly code: string;
|
|
1141
2053
|
readonly statusCode?: number;
|
|
1142
2054
|
readonly details?: Record<string, unknown>;
|
|
1143
|
-
|
|
2055
|
+
/** Request ID for debugging with support */
|
|
2056
|
+
readonly requestId?: string;
|
|
2057
|
+
constructor(message: string, code: string, statusCode?: number, details?: Record<string, unknown>, requestId?: string);
|
|
1144
2058
|
}
|
|
1145
2059
|
/**
|
|
1146
2060
|
* Thrown when API key is missing or invalid
|
|
1147
2061
|
*/
|
|
1148
2062
|
declare class AuthenticationError extends DispatchTicketsError {
|
|
1149
|
-
constructor(message?: string);
|
|
2063
|
+
constructor(message?: string, requestId?: string);
|
|
1150
2064
|
}
|
|
1151
2065
|
/**
|
|
1152
2066
|
* Thrown when rate limit is exceeded
|
|
1153
2067
|
*/
|
|
1154
2068
|
declare class RateLimitError extends DispatchTicketsError {
|
|
1155
2069
|
readonly retryAfter?: number;
|
|
1156
|
-
|
|
2070
|
+
/** Rate limit ceiling */
|
|
2071
|
+
readonly limit?: number;
|
|
2072
|
+
/** Remaining requests in current window */
|
|
2073
|
+
readonly remaining?: number;
|
|
2074
|
+
/** Unix timestamp when rate limit resets */
|
|
2075
|
+
readonly reset?: number;
|
|
2076
|
+
constructor(message?: string, retryAfter?: number, requestId?: string, rateLimitInfo?: {
|
|
2077
|
+
limit?: number;
|
|
2078
|
+
remaining?: number;
|
|
2079
|
+
reset?: number;
|
|
2080
|
+
});
|
|
1157
2081
|
}
|
|
1158
2082
|
/**
|
|
1159
2083
|
* Thrown when request validation fails
|
|
@@ -1166,7 +2090,7 @@ declare class ValidationError extends DispatchTicketsError {
|
|
|
1166
2090
|
constructor(message?: string, errors?: Array<{
|
|
1167
2091
|
field: string;
|
|
1168
2092
|
message: string;
|
|
1169
|
-
}
|
|
2093
|
+
}>, requestId?: string);
|
|
1170
2094
|
}
|
|
1171
2095
|
/**
|
|
1172
2096
|
* Thrown when a resource is not found
|
|
@@ -1174,19 +2098,19 @@ declare class ValidationError extends DispatchTicketsError {
|
|
|
1174
2098
|
declare class NotFoundError extends DispatchTicketsError {
|
|
1175
2099
|
readonly resourceType?: string;
|
|
1176
2100
|
readonly resourceId?: string;
|
|
1177
|
-
constructor(message?: string, resourceType?: string, resourceId?: string);
|
|
2101
|
+
constructor(message?: string, resourceType?: string, resourceId?: string, requestId?: string);
|
|
1178
2102
|
}
|
|
1179
2103
|
/**
|
|
1180
2104
|
* Thrown when there's a conflict (e.g., duplicate resource)
|
|
1181
2105
|
*/
|
|
1182
2106
|
declare class ConflictError extends DispatchTicketsError {
|
|
1183
|
-
constructor(message?: string);
|
|
2107
|
+
constructor(message?: string, requestId?: string);
|
|
1184
2108
|
}
|
|
1185
2109
|
/**
|
|
1186
2110
|
* Thrown when the server returns an unexpected error
|
|
1187
2111
|
*/
|
|
1188
2112
|
declare class ServerError extends DispatchTicketsError {
|
|
1189
|
-
constructor(message?: string, statusCode?: number);
|
|
2113
|
+
constructor(message?: string, statusCode?: number, requestId?: string);
|
|
1190
2114
|
}
|
|
1191
2115
|
/**
|
|
1192
2116
|
* Thrown when request times out
|
|
@@ -1200,6 +2124,42 @@ declare class TimeoutError extends DispatchTicketsError {
|
|
|
1200
2124
|
declare class NetworkError extends DispatchTicketsError {
|
|
1201
2125
|
constructor(message?: string);
|
|
1202
2126
|
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Check if an error is a DispatchTicketsError
|
|
2129
|
+
*/
|
|
2130
|
+
declare function isDispatchTicketsError(error: unknown): error is DispatchTicketsError;
|
|
2131
|
+
/**
|
|
2132
|
+
* Check if an error is an AuthenticationError
|
|
2133
|
+
*/
|
|
2134
|
+
declare function isAuthenticationError(error: unknown): error is AuthenticationError;
|
|
2135
|
+
/**
|
|
2136
|
+
* Check if an error is a RateLimitError
|
|
2137
|
+
*/
|
|
2138
|
+
declare function isRateLimitError(error: unknown): error is RateLimitError;
|
|
2139
|
+
/**
|
|
2140
|
+
* Check if an error is a ValidationError
|
|
2141
|
+
*/
|
|
2142
|
+
declare function isValidationError(error: unknown): error is ValidationError;
|
|
2143
|
+
/**
|
|
2144
|
+
* Check if an error is a NotFoundError
|
|
2145
|
+
*/
|
|
2146
|
+
declare function isNotFoundError(error: unknown): error is NotFoundError;
|
|
2147
|
+
/**
|
|
2148
|
+
* Check if an error is a ConflictError
|
|
2149
|
+
*/
|
|
2150
|
+
declare function isConflictError(error: unknown): error is ConflictError;
|
|
2151
|
+
/**
|
|
2152
|
+
* Check if an error is a ServerError
|
|
2153
|
+
*/
|
|
2154
|
+
declare function isServerError(error: unknown): error is ServerError;
|
|
2155
|
+
/**
|
|
2156
|
+
* Check if an error is a TimeoutError
|
|
2157
|
+
*/
|
|
2158
|
+
declare function isTimeoutError(error: unknown): error is TimeoutError;
|
|
2159
|
+
/**
|
|
2160
|
+
* Check if an error is a NetworkError
|
|
2161
|
+
*/
|
|
2162
|
+
declare function isNetworkError(error: unknown): error is NetworkError;
|
|
1203
2163
|
|
|
1204
2164
|
/**
|
|
1205
2165
|
* All supported webhook event types
|
|
@@ -1379,4 +2339,4 @@ declare const webhookUtils: {
|
|
|
1379
2339
|
*/
|
|
1380
2340
|
declare function collectAll<T>(iterable: AsyncIterable<T>): Promise<T[]>;
|
|
1381
2341
|
|
|
1382
|
-
export { type Account, type AccountUsage, type ApiKey, type ApiKeyWithSecret, type Attachment, type AttachmentStatus, type AttachmentWithUrl, AuthenticationError, type AuthorType, type Brand, type BulkAction, type BulkActionResult, type Category, type CategoryStats, type Comment, type CommentCreatedData, type CommentCreatedEvent, type Company, ConflictError, type CreateApiKeyInput, type CreateBrandInput, type CreateCategoryInput, type CreateCommentInput, type CreateCommentOptions, type CreateCustomerInput, type CreateFieldInput, type CreateTagInput, type CreateTicketInput, type CreateTicketOptions, type CreateWebhookInput, type Customer, type DeleteBrandPreview, DispatchTickets, type DispatchTicketsConfig, DispatchTicketsError, type EntityType, type EventCommentData, type EventCustomerInfo, type FieldDefinition, type FieldDefinitions, type FieldType, type InitiateUploadInput, type InitiateUploadResponse, type Link, type ListCustomersFilters, type ListTicketsFilters, type MergeTagsInput, type MergeTicketsInput, NetworkError, NotFoundError, type PaginatedResponse, RateLimitError, ServerError, type SortOrder, type Tag, type Ticket, type TicketCreatedData, type TicketCreatedEvent, type TicketPriority, type TicketSource, type TicketStatus, type TicketUpdatedData, type TicketUpdatedEvent, TimeoutError, type UpdateApiKeyScopeInput, type UpdateBrandInput, type UpdateCategoryInput, type UpdateCommentInput, type UpdateCustomerInput, type UpdateFieldInput, type UpdateTagInput, type UpdateTicketInput, ValidationError, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookEventEnvelope, type WebhookEventMap, type WebhookEventName, type WebhookEventType, collectAll, isCommentCreatedEvent, isTicketCreatedEvent, isTicketUpdatedEvent, parseWebhookEvent, webhookUtils };
|
|
2342
|
+
export { type Account, type AccountUsage, type ApiKey, type ApiKeyWithSecret, type ApiRequestOptions, type Attachment, type AttachmentStatus, type AttachmentWithUrl, AuthenticationError, type AuthorType, type Brand, type BulkAction, type BulkActionResult, type Category, type CategoryStats, type Comment, type CommentCreatedData, type CommentCreatedEvent, type Company, ConflictError, type CreateApiKeyInput, type CreateBrandInput, type CreateCategoryInput, type CreateCommentInput, type CreateCommentOptions, type CreateCustomerInput, type CreateFieldInput, type CreateTagInput, type CreateTicketInput, type CreateTicketOptions, type CreateWebhookInput, type Customer, type DeleteBrandPreview, DispatchPortal, type DispatchPortalConfig, DispatchTickets, type DispatchTicketsConfig, DispatchTicketsError, type EntityType, type EventCommentData, type EventCustomerInfo, type FieldDefinition, type FieldDefinitions, type FieldType, type GeneratePortalTokenInput, type Hooks, type InitiateUploadInput, type InitiateUploadResponse, type Link, type ListCustomersFilters, type ListTicketsFilters, type MergeTagsInput, type MergeTicketsInput, NetworkError, NotFoundError, type PaginatedResponse, type PortalComment, type PortalCreateTicketInput, type PortalListTicketsFilters, type PortalTicket, type PortalTicketDetail, type PortalTicketListResponse, type PortalTokenResponse, RateLimitError, type RateLimitInfo, type RequestContext, type ResponseContext, type RetryConfig, type SendMagicLinkInput, ServerError, type SortOrder, type Tag, type Ticket, type TicketCreatedData, type TicketCreatedEvent, type TicketPriority, type TicketSource, type TicketStatus, type TicketUpdatedData, type TicketUpdatedEvent, TimeoutError, type UpdateApiKeyScopeInput, type UpdateBrandInput, type UpdateCategoryInput, type UpdateCommentInput, type UpdateCustomerInput, type UpdateFieldInput, type UpdateTagInput, type UpdateTicketInput, ValidationError, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookEventEnvelope, type WebhookEventMap, type WebhookEventName, type WebhookEventType, collectAll, isAuthenticationError, isCommentCreatedEvent, isConflictError, isDispatchTicketsError, isNetworkError, isNotFoundError, isRateLimitError, isServerError, isTicketCreatedEvent, isTicketUpdatedEvent, isTimeoutError, isValidationError, parseWebhookEvent, webhookUtils };
|