@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/dist/index.cjs CHANGED
@@ -7,33 +7,45 @@ var DispatchTicketsError = class extends Error {
7
7
  code;
8
8
  statusCode;
9
9
  details;
10
- constructor(message, code, statusCode, details) {
10
+ /** Request ID for debugging with support */
11
+ requestId;
12
+ constructor(message, code, statusCode, details, requestId) {
11
13
  super(message);
12
14
  this.name = "DispatchTicketsError";
13
15
  this.code = code;
14
16
  this.statusCode = statusCode;
15
17
  this.details = details;
18
+ this.requestId = requestId;
16
19
  Object.setPrototypeOf(this, new.target.prototype);
17
20
  }
18
21
  };
19
22
  var AuthenticationError = class extends DispatchTicketsError {
20
- constructor(message = "Invalid or missing API key") {
21
- super(message, "authentication_error", 401);
23
+ constructor(message = "Invalid or missing API key", requestId) {
24
+ super(message, "authentication_error", 401, void 0, requestId);
22
25
  this.name = "AuthenticationError";
23
26
  }
24
27
  };
25
28
  var RateLimitError = class extends DispatchTicketsError {
26
29
  retryAfter;
27
- constructor(message = "Rate limit exceeded", retryAfter) {
28
- super(message, "rate_limit_error", 429);
30
+ /** Rate limit ceiling */
31
+ limit;
32
+ /** Remaining requests in current window */
33
+ remaining;
34
+ /** Unix timestamp when rate limit resets */
35
+ reset;
36
+ constructor(message = "Rate limit exceeded", retryAfter, requestId, rateLimitInfo) {
37
+ super(message, "rate_limit_error", 429, void 0, requestId);
29
38
  this.name = "RateLimitError";
30
39
  this.retryAfter = retryAfter;
40
+ this.limit = rateLimitInfo?.limit;
41
+ this.remaining = rateLimitInfo?.remaining;
42
+ this.reset = rateLimitInfo?.reset;
31
43
  }
32
44
  };
33
45
  var ValidationError = class extends DispatchTicketsError {
34
46
  errors;
35
- constructor(message = "Validation failed", errors) {
36
- super(message, "validation_error", 400, { errors });
47
+ constructor(message = "Validation failed", errors, requestId) {
48
+ super(message, "validation_error", 400, { errors }, requestId);
37
49
  this.name = "ValidationError";
38
50
  this.errors = errors;
39
51
  }
@@ -41,22 +53,22 @@ var ValidationError = class extends DispatchTicketsError {
41
53
  var NotFoundError = class extends DispatchTicketsError {
42
54
  resourceType;
43
55
  resourceId;
44
- constructor(message = "Resource not found", resourceType, resourceId) {
45
- super(message, "not_found", 404, { resourceType, resourceId });
56
+ constructor(message = "Resource not found", resourceType, resourceId, requestId) {
57
+ super(message, "not_found", 404, { resourceType, resourceId }, requestId);
46
58
  this.name = "NotFoundError";
47
59
  this.resourceType = resourceType;
48
60
  this.resourceId = resourceId;
49
61
  }
50
62
  };
51
63
  var ConflictError = class extends DispatchTicketsError {
52
- constructor(message = "Resource conflict") {
53
- super(message, "conflict", 409);
64
+ constructor(message = "Resource conflict", requestId) {
65
+ super(message, "conflict", 409, void 0, requestId);
54
66
  this.name = "ConflictError";
55
67
  }
56
68
  };
57
69
  var ServerError = class extends DispatchTicketsError {
58
- constructor(message = "Internal server error", statusCode = 500) {
59
- super(message, "server_error", statusCode);
70
+ constructor(message = "Internal server error", statusCode = 500, requestId) {
71
+ super(message, "server_error", statusCode, void 0, requestId);
60
72
  this.name = "ServerError";
61
73
  }
62
74
  };
@@ -72,12 +84,68 @@ var NetworkError = class extends DispatchTicketsError {
72
84
  this.name = "NetworkError";
73
85
  }
74
86
  };
87
+ function isDispatchTicketsError(error) {
88
+ return error instanceof DispatchTicketsError;
89
+ }
90
+ function isAuthenticationError(error) {
91
+ return error instanceof AuthenticationError;
92
+ }
93
+ function isRateLimitError(error) {
94
+ return error instanceof RateLimitError;
95
+ }
96
+ function isValidationError(error) {
97
+ return error instanceof ValidationError;
98
+ }
99
+ function isNotFoundError(error) {
100
+ return error instanceof NotFoundError;
101
+ }
102
+ function isConflictError(error) {
103
+ return error instanceof ConflictError;
104
+ }
105
+ function isServerError(error) {
106
+ return error instanceof ServerError;
107
+ }
108
+ function isTimeoutError(error) {
109
+ return error instanceof TimeoutError;
110
+ }
111
+ function isNetworkError(error) {
112
+ return error instanceof NetworkError;
113
+ }
75
114
 
76
115
  // src/utils/http.ts
77
116
  var HttpClient = class {
78
117
  config;
118
+ fetchFn;
119
+ retryConfig;
120
+ /** Rate limit info from the last response */
121
+ _lastRateLimit;
122
+ /** Request ID from the last response */
123
+ _lastRequestId;
79
124
  constructor(config) {
80
125
  this.config = config;
126
+ this.fetchFn = config.fetch ?? fetch;
127
+ this.retryConfig = {
128
+ maxRetries: config.retry?.maxRetries ?? config.maxRetries,
129
+ retryableStatuses: config.retry?.retryableStatuses ?? [429, 500, 502, 503, 504],
130
+ retryOnNetworkError: config.retry?.retryOnNetworkError ?? true,
131
+ retryOnTimeout: config.retry?.retryOnTimeout ?? true,
132
+ initialDelayMs: config.retry?.initialDelayMs ?? 1e3,
133
+ maxDelayMs: config.retry?.maxDelayMs ?? 3e4,
134
+ backoffMultiplier: config.retry?.backoffMultiplier ?? 2,
135
+ jitter: config.retry?.jitter ?? 0.25
136
+ };
137
+ }
138
+ /**
139
+ * Get rate limit info from the last response
140
+ */
141
+ get lastRateLimit() {
142
+ return this._lastRateLimit;
143
+ }
144
+ /**
145
+ * Get request ID from the last response
146
+ */
147
+ get lastRequestId() {
148
+ return this._lastRequestId;
81
149
  }
82
150
  /**
83
151
  * Execute an HTTP request with retry logic
@@ -87,43 +155,89 @@ var HttpClient = class {
87
155
  const headers = this.buildHeaders(options.headers, options.idempotencyKey);
88
156
  let lastError;
89
157
  let attempt = 0;
90
- while (attempt <= this.config.maxRetries) {
158
+ while (attempt <= this.retryConfig.maxRetries) {
159
+ const requestContext = {
160
+ method: options.method,
161
+ url,
162
+ headers,
163
+ body: options.body,
164
+ attempt
165
+ };
91
166
  try {
92
- const response = await this.executeRequest(url, options.method, headers, options.body);
93
- return await this.handleResponse(response);
167
+ if (this.config.hooks?.onRequest) {
168
+ await this.config.hooks.onRequest(requestContext);
169
+ }
170
+ const startTime = Date.now();
171
+ const response = await this.executeRequest(
172
+ url,
173
+ options.method,
174
+ headers,
175
+ options.body,
176
+ options.signal
177
+ );
178
+ const durationMs = Date.now() - startTime;
179
+ const result = await this.handleResponse(response, requestContext, durationMs);
180
+ return result;
94
181
  } catch (error) {
95
182
  lastError = error;
96
- if (error instanceof DispatchTicketsError) {
97
- if (error instanceof AuthenticationError || error instanceof ValidationError || error instanceof NotFoundError || error instanceof ConflictError) {
98
- throw error;
99
- }
100
- if (error instanceof RateLimitError && error.retryAfter) {
101
- if (attempt < this.config.maxRetries) {
102
- await this.sleep(error.retryAfter * 1e3);
103
- attempt++;
104
- continue;
105
- }
106
- }
107
- if (error instanceof ServerError) {
108
- if (attempt < this.config.maxRetries) {
109
- await this.sleep(this.calculateBackoff(attempt));
110
- attempt++;
111
- continue;
112
- }
113
- }
183
+ if (this.config.hooks?.onError) {
184
+ await this.config.hooks.onError(lastError, requestContext);
114
185
  }
115
- if (error instanceof NetworkError || error instanceof TimeoutError) {
116
- if (attempt < this.config.maxRetries) {
117
- await this.sleep(this.calculateBackoff(attempt));
118
- attempt++;
119
- continue;
186
+ if (attempt < this.retryConfig.maxRetries && this.shouldRetry(lastError)) {
187
+ const delay = this.calculateDelay(lastError, attempt);
188
+ if (this.config.hooks?.onRetry) {
189
+ await this.config.hooks.onRetry(requestContext, lastError, delay);
120
190
  }
191
+ await this.sleep(delay);
192
+ attempt++;
193
+ continue;
121
194
  }
122
- throw error;
195
+ throw lastError;
123
196
  }
124
197
  }
125
198
  throw lastError || new NetworkError("Request failed after retries");
126
199
  }
200
+ /**
201
+ * Execute request and return response with rate limit info
202
+ */
203
+ async requestWithRateLimit(options) {
204
+ const data = await this.request(options);
205
+ return {
206
+ data,
207
+ rateLimit: this._lastRateLimit,
208
+ requestId: this._lastRequestId
209
+ };
210
+ }
211
+ shouldRetry(error) {
212
+ if (error instanceof AuthenticationError || error instanceof ValidationError || error instanceof NotFoundError || error instanceof ConflictError) {
213
+ return false;
214
+ }
215
+ if (error instanceof RateLimitError) {
216
+ return true;
217
+ }
218
+ if (error instanceof ServerError && error.statusCode) {
219
+ return this.retryConfig.retryableStatuses.includes(error.statusCode);
220
+ }
221
+ if (error instanceof NetworkError) {
222
+ return this.retryConfig.retryOnNetworkError;
223
+ }
224
+ if (error instanceof TimeoutError) {
225
+ return this.retryConfig.retryOnTimeout;
226
+ }
227
+ return false;
228
+ }
229
+ calculateDelay(error, attempt) {
230
+ if (error instanceof RateLimitError && error.retryAfter) {
231
+ return error.retryAfter * 1e3;
232
+ }
233
+ const baseDelay = this.retryConfig.initialDelayMs;
234
+ const delay = Math.min(
235
+ baseDelay * Math.pow(this.retryConfig.backoffMultiplier, attempt),
236
+ this.retryConfig.maxDelayMs
237
+ );
238
+ const jitter = delay * this.retryConfig.jitter * Math.random();
239
+ return delay + jitter;
240
+ }
127
241
  buildUrl(path, query) {
128
242
  const url = new URL(path, this.config.baseUrl);
129
243
  if (query) {
@@ -147,9 +261,11 @@ var HttpClient = class {
147
261
  }
148
262
  return headers;
149
263
  }
150
- async executeRequest(url, method, headers, body) {
264
+ async executeRequest(url, method, headers, body, userSignal) {
151
265
  const controller = new AbortController();
152
266
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
267
+ const abortHandler = () => controller.abort();
268
+ userSignal?.addEventListener("abort", abortHandler);
153
269
  try {
154
270
  if (this.config.debug) {
155
271
  console.log(`[DispatchTickets] ${method} ${url}`);
@@ -157,7 +273,10 @@ var HttpClient = class {
157
273
  console.log("[DispatchTickets] Body:", JSON.stringify(body, null, 2));
158
274
  }
159
275
  }
160
- const response = await fetch(url, {
276
+ if (userSignal?.aborted) {
277
+ throw new Error("Request aborted");
278
+ }
279
+ const response = await this.fetchFn(url, {
161
280
  method,
162
281
  headers,
163
282
  body: body ? JSON.stringify(body) : void 0,
@@ -167,6 +286,9 @@ var HttpClient = class {
167
286
  } catch (error) {
168
287
  if (error instanceof Error) {
169
288
  if (error.name === "AbortError") {
289
+ if (userSignal?.aborted) {
290
+ throw new NetworkError("Request aborted by user");
291
+ }
170
292
  throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`);
171
293
  }
172
294
  throw new NetworkError(error.message);
@@ -174,11 +296,42 @@ var HttpClient = class {
174
296
  throw new NetworkError("Unknown network error");
175
297
  } finally {
176
298
  clearTimeout(timeoutId);
299
+ userSignal?.removeEventListener("abort", abortHandler);
300
+ }
301
+ }
302
+ extractRateLimitInfo(response) {
303
+ const limit = response.headers.get("x-ratelimit-limit");
304
+ const remaining = response.headers.get("x-ratelimit-remaining");
305
+ const reset = response.headers.get("x-ratelimit-reset");
306
+ if (limit && remaining && reset) {
307
+ return {
308
+ limit: parseInt(limit, 10),
309
+ remaining: parseInt(remaining, 10),
310
+ reset: parseInt(reset, 10)
311
+ };
177
312
  }
313
+ return void 0;
178
314
  }
179
- async handleResponse(response) {
315
+ async handleResponse(response, requestContext, durationMs) {
180
316
  const contentType = response.headers.get("content-type");
181
317
  const isJson = contentType?.includes("application/json");
318
+ const requestId = response.headers.get("x-request-id") ?? void 0;
319
+ const rateLimitInfo = this.extractRateLimitInfo(response);
320
+ this._lastRequestId = requestId;
321
+ this._lastRateLimit = rateLimitInfo;
322
+ if (this.config.debug && requestId) {
323
+ console.log(`[DispatchTickets] Request ID: ${requestId}`);
324
+ }
325
+ if (response.ok && this.config.hooks?.onResponse) {
326
+ await this.config.hooks.onResponse({
327
+ request: requestContext,
328
+ status: response.status,
329
+ headers: response.headers,
330
+ requestId,
331
+ rateLimit: rateLimitInfo,
332
+ durationMs
333
+ });
334
+ }
182
335
  if (response.ok) {
183
336
  if (response.status === 204 || !isJson) {
184
337
  return void 0;
@@ -195,31 +348,36 @@ var HttpClient = class {
195
348
  const message = errorData.message || errorData.error || response.statusText;
196
349
  switch (response.status) {
197
350
  case 401:
198
- throw new AuthenticationError(message);
351
+ throw new AuthenticationError(message, requestId);
199
352
  case 400:
200
353
  case 422:
201
- throw new ValidationError(message, errorData.errors);
354
+ throw new ValidationError(message, errorData.errors, requestId);
202
355
  case 404:
203
- throw new NotFoundError(message);
356
+ throw new NotFoundError(message, void 0, void 0, requestId);
204
357
  case 409:
205
- throw new ConflictError(message);
358
+ throw new ConflictError(message, requestId);
206
359
  case 429: {
207
360
  const retryAfter = response.headers.get("retry-after");
208
- throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : void 0);
361
+ throw new RateLimitError(
362
+ message,
363
+ retryAfter ? parseInt(retryAfter, 10) : void 0,
364
+ requestId,
365
+ rateLimitInfo
366
+ );
209
367
  }
210
368
  default:
211
369
  if (response.status >= 500) {
212
- throw new ServerError(message, response.status);
370
+ throw new ServerError(message, response.status, requestId);
213
371
  }
214
- throw new DispatchTicketsError(message, "api_error", response.status, errorData);
372
+ throw new DispatchTicketsError(
373
+ message,
374
+ "api_error",
375
+ response.status,
376
+ errorData,
377
+ requestId
378
+ );
215
379
  }
216
380
  }
217
- calculateBackoff(attempt) {
218
- const baseDelay = 1e3;
219
- const maxDelay = 3e4;
220
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
221
- return delay + Math.random() * delay * 0.25;
222
- }
223
381
  sleep(ms) {
224
382
  return new Promise((resolve) => setTimeout(resolve, ms));
225
383
  }
@@ -231,11 +389,12 @@ var BaseResource = class {
231
389
  constructor(http) {
232
390
  this.http = http;
233
391
  }
234
- async _get(path, query) {
392
+ async _get(path, query, options) {
235
393
  return this.http.request({
236
394
  method: "GET",
237
395
  path,
238
- query
396
+ query,
397
+ signal: options?.signal
239
398
  });
240
399
  }
241
400
  async _post(path, body, options) {
@@ -244,32 +403,82 @@ var BaseResource = class {
244
403
  path,
245
404
  body,
246
405
  idempotencyKey: options?.idempotencyKey,
247
- query: options?.query
406
+ query: options?.query,
407
+ signal: options?.signal
248
408
  });
249
409
  }
250
- async _patch(path, body) {
410
+ async _patch(path, body, options) {
251
411
  return this.http.request({
252
412
  method: "PATCH",
253
413
  path,
254
- body
414
+ body,
415
+ signal: options?.signal
255
416
  });
256
417
  }
257
- async _put(path, body) {
418
+ async _put(path, body, options) {
258
419
  return this.http.request({
259
420
  method: "PUT",
260
421
  path,
261
- body
422
+ body,
423
+ signal: options?.signal
262
424
  });
263
425
  }
264
- async _delete(path, query) {
426
+ async _delete(path, query, options) {
265
427
  return this.http.request({
266
428
  method: "DELETE",
267
429
  path,
268
- query
430
+ query,
431
+ signal: options?.signal
269
432
  });
270
433
  }
271
434
  };
272
435
 
436
+ // src/resources/accounts.ts
437
+ var AccountsResource = class extends BaseResource {
438
+ /**
439
+ * Get the current account
440
+ */
441
+ async me() {
442
+ return this._get("/accounts/me");
443
+ }
444
+ /**
445
+ * Get usage statistics for the current account
446
+ */
447
+ async getUsage() {
448
+ return this._get("/accounts/me/usage");
449
+ }
450
+ /**
451
+ * List all API keys for the current account
452
+ */
453
+ async listApiKeys() {
454
+ return this._get("/accounts/me/api-keys");
455
+ }
456
+ /**
457
+ * Create a new API key
458
+ *
459
+ * Note: The full key value is only returned once on creation.
460
+ * Store it securely as it cannot be retrieved again.
461
+ */
462
+ async createApiKey(data) {
463
+ return this._post("/accounts/me/api-keys", data);
464
+ }
465
+ /**
466
+ * Update the brand scope for an API key
467
+ */
468
+ async updateApiKeyScope(keyId, data) {
469
+ return this._patch(
470
+ `/accounts/me/api-keys/${keyId}/scope`,
471
+ data
472
+ );
473
+ }
474
+ /**
475
+ * Revoke an API key
476
+ */
477
+ async revokeApiKey(keyId) {
478
+ await this._delete(`/accounts/me/api-keys/${keyId}`);
479
+ }
480
+ };
481
+
273
482
  // src/resources/brands.ts
274
483
  var BrandsResource = class extends BaseResource {
275
484
  /**
@@ -320,6 +529,28 @@ var BrandsResource = class extends BaseResource {
320
529
  async updateSchema(brandId, schema) {
321
530
  return this._put(`/brands/${brandId}/schema`, schema);
322
531
  }
532
+ /**
533
+ * Get the inbound email address for a brand
534
+ *
535
+ * Emails sent to this address will automatically create tickets.
536
+ *
537
+ * @param brandId - The brand ID
538
+ * @param domain - Optional custom inbound domain (default: inbound.dispatchtickets.com)
539
+ * @returns The inbound email address
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * const email = client.brands.getInboundEmail('br_abc123');
544
+ * // Returns: br_abc123@inbound.dispatchtickets.com
545
+ *
546
+ * // With custom domain:
547
+ * const customEmail = client.brands.getInboundEmail('br_abc123', 'support.mycompany.com');
548
+ * // Returns: br_abc123@support.mycompany.com
549
+ * ```
550
+ */
551
+ getInboundEmail(brandId, domain = "inbound.dispatchtickets.com") {
552
+ return `${brandId}@${domain}`;
553
+ }
323
554
  };
324
555
 
325
556
  // src/resources/tickets.ts
@@ -834,46 +1065,209 @@ var webhookUtils = {
834
1065
  // src/client.ts
835
1066
  var DispatchTickets = class {
836
1067
  http;
1068
+ /**
1069
+ * Accounts resource for managing the current account and API keys
1070
+ *
1071
+ * @example
1072
+ * ```typescript
1073
+ * // Get current account
1074
+ * const account = await client.accounts.me();
1075
+ *
1076
+ * // Get usage statistics
1077
+ * const usage = await client.accounts.getUsage();
1078
+ *
1079
+ * // Create a new API key
1080
+ * const newKey = await client.accounts.createApiKey({
1081
+ * name: 'Production',
1082
+ * allBrands: true,
1083
+ * });
1084
+ * ```
1085
+ */
1086
+ accounts;
837
1087
  /**
838
1088
  * Brands (workspaces) resource
1089
+ *
1090
+ * Brands are isolated containers for tickets. Each brand can have its own
1091
+ * email address, categories, tags, and settings.
1092
+ *
1093
+ * @example
1094
+ * ```typescript
1095
+ * // List all brands
1096
+ * const brands = await client.brands.list();
1097
+ *
1098
+ * // Create a new brand
1099
+ * const brand = await client.brands.create({
1100
+ * name: 'Acme Support',
1101
+ * slug: 'acme',
1102
+ * });
1103
+ *
1104
+ * // Get inbound email address
1105
+ * const email = client.brands.getInboundEmail('br_abc123');
1106
+ * // Returns: br_abc123@inbound.dispatchtickets.com
1107
+ * ```
839
1108
  */
840
1109
  brands;
841
1110
  /**
842
1111
  * Tickets resource
1112
+ *
1113
+ * @example
1114
+ * ```typescript
1115
+ * // Create a ticket
1116
+ * const ticket = await client.tickets.create('ws_abc123', {
1117
+ * title: 'Issue with billing',
1118
+ * body: 'I was charged twice...',
1119
+ * priority: 'high',
1120
+ * });
1121
+ *
1122
+ * // Iterate through all tickets
1123
+ * for await (const ticket of client.tickets.list('ws_abc123', { status: 'open' })) {
1124
+ * console.log(ticket.title);
1125
+ * }
1126
+ * ```
843
1127
  */
844
1128
  tickets;
845
1129
  /**
846
1130
  * Comments resource
1131
+ *
1132
+ * @example
1133
+ * ```typescript
1134
+ * // Add a comment
1135
+ * const comment = await client.comments.create('ws_abc123', 'tkt_xyz', {
1136
+ * body: 'Thanks for your patience!',
1137
+ * authorType: 'AGENT',
1138
+ * });
1139
+ *
1140
+ * // List comments
1141
+ * const comments = await client.comments.list('ws_abc123', 'tkt_xyz');
1142
+ * ```
847
1143
  */
848
1144
  comments;
849
1145
  /**
850
1146
  * Attachments resource
1147
+ *
1148
+ * @example
1149
+ * ```typescript
1150
+ * // Simple upload
1151
+ * const attachment = await client.attachments.upload(
1152
+ * 'ws_abc123',
1153
+ * 'tkt_xyz',
1154
+ * fileBuffer,
1155
+ * 'document.pdf',
1156
+ * 'application/pdf'
1157
+ * );
1158
+ *
1159
+ * // Get download URL
1160
+ * const { downloadUrl } = await client.attachments.get('ws_abc123', 'tkt_xyz', 'att_abc');
1161
+ * ```
851
1162
  */
852
1163
  attachments;
853
1164
  /**
854
1165
  * Webhooks resource
1166
+ *
1167
+ * @example
1168
+ * ```typescript
1169
+ * // Create a webhook
1170
+ * const webhook = await client.webhooks.create('ws_abc123', {
1171
+ * url: 'https://example.com/webhook',
1172
+ * secret: 'your-secret',
1173
+ * events: ['ticket.created', 'ticket.updated'],
1174
+ * });
1175
+ * ```
855
1176
  */
856
1177
  webhooks;
857
1178
  /**
858
1179
  * Categories resource
1180
+ *
1181
+ * @example
1182
+ * ```typescript
1183
+ * // Create a category
1184
+ * await client.categories.create('ws_abc123', { name: 'Billing', color: '#ef4444' });
1185
+ *
1186
+ * // Get category stats
1187
+ * const stats = await client.categories.getStats('ws_abc123');
1188
+ * ```
859
1189
  */
860
1190
  categories;
861
1191
  /**
862
1192
  * Tags resource
1193
+ *
1194
+ * @example
1195
+ * ```typescript
1196
+ * // Create a tag
1197
+ * await client.tags.create('ws_abc123', { name: 'urgent', color: '#f59e0b' });
1198
+ *
1199
+ * // Merge tags
1200
+ * await client.tags.merge('ws_abc123', 'tag_target', ['tag_source1', 'tag_source2']);
1201
+ * ```
863
1202
  */
864
1203
  tags;
865
1204
  /**
866
1205
  * Customers resource
1206
+ *
1207
+ * @example
1208
+ * ```typescript
1209
+ * // Create a customer
1210
+ * const customer = await client.customers.create('ws_abc123', {
1211
+ * email: 'user@example.com',
1212
+ * name: 'Jane Doe',
1213
+ * });
1214
+ *
1215
+ * // Search customers
1216
+ * const results = await client.customers.search('ws_abc123', 'jane');
1217
+ * ```
867
1218
  */
868
1219
  customers;
869
1220
  /**
870
1221
  * Custom fields resource
1222
+ *
1223
+ * @example
1224
+ * ```typescript
1225
+ * // Get all field definitions
1226
+ * const fields = await client.fields.getAll('ws_abc123');
1227
+ *
1228
+ * // Create a field
1229
+ * await client.fields.create('ws_abc123', 'ticket', {
1230
+ * key: 'order_id',
1231
+ * label: 'Order ID',
1232
+ * type: 'text',
1233
+ * required: true,
1234
+ * });
1235
+ * ```
871
1236
  */
872
1237
  fields;
873
1238
  /**
874
1239
  * Static webhook utilities
1240
+ *
1241
+ * @example
1242
+ * ```typescript
1243
+ * // Verify webhook signature
1244
+ * const isValid = DispatchTickets.webhooks.verifySignature(
1245
+ * rawBody,
1246
+ * req.headers['x-dispatch-signature'],
1247
+ * 'your-secret'
1248
+ * );
1249
+ *
1250
+ * // Generate signature for testing
1251
+ * const signature = DispatchTickets.webhooks.generateSignature(
1252
+ * JSON.stringify(payload),
1253
+ * 'your-secret'
1254
+ * );
1255
+ * ```
875
1256
  */
876
1257
  static webhooks = webhookUtils;
1258
+ /**
1259
+ * Create a new Dispatch Tickets client
1260
+ *
1261
+ * @param config - Client configuration options
1262
+ * @throws Error if API key is not provided
1263
+ *
1264
+ * @example
1265
+ * ```typescript
1266
+ * const client = new DispatchTickets({
1267
+ * apiKey: 'sk_live_...',
1268
+ * });
1269
+ * ```
1270
+ */
877
1271
  constructor(config) {
878
1272
  if (!config.apiKey) {
879
1273
  throw new Error("API key is required");
@@ -882,10 +1276,14 @@ var DispatchTickets = class {
882
1276
  baseUrl: config.baseUrl || "https://dispatch-tickets-api.onrender.com/v1",
883
1277
  apiKey: config.apiKey,
884
1278
  timeout: config.timeout ?? 3e4,
885
- maxRetries: config.maxRetries ?? 3,
886
- debug: config.debug ?? false
1279
+ maxRetries: config.maxRetries ?? config.retry?.maxRetries ?? 3,
1280
+ debug: config.debug ?? false,
1281
+ fetch: config.fetch,
1282
+ retry: config.retry,
1283
+ hooks: config.hooks
887
1284
  };
888
1285
  this.http = new HttpClient(httpConfig);
1286
+ this.accounts = new AccountsResource(this.http);
889
1287
  this.brands = new BrandsResource(this.http);
890
1288
  this.tickets = new TicketsResource(this.http);
891
1289
  this.comments = new CommentsResource(this.http);
@@ -898,6 +1296,37 @@ var DispatchTickets = class {
898
1296
  }
899
1297
  };
900
1298
 
1299
+ // src/types/events.ts
1300
+ function isTicketCreatedEvent(event) {
1301
+ return event.event === "ticket.created";
1302
+ }
1303
+ function isTicketUpdatedEvent(event) {
1304
+ return event.event === "ticket.updated";
1305
+ }
1306
+ function isCommentCreatedEvent(event) {
1307
+ return event.event === "ticket.comment.created";
1308
+ }
1309
+ function parseWebhookEvent(payload) {
1310
+ const event = typeof payload === "string" ? JSON.parse(payload) : payload;
1311
+ if (!event || typeof event !== "object") {
1312
+ throw new Error("Invalid webhook payload: expected object");
1313
+ }
1314
+ if (!event.event || typeof event.event !== "string") {
1315
+ throw new Error("Invalid webhook payload: missing event type");
1316
+ }
1317
+ if (!event.id || typeof event.id !== "string") {
1318
+ throw new Error("Invalid webhook payload: missing event id");
1319
+ }
1320
+ if (!event.data || typeof event.data !== "object") {
1321
+ throw new Error("Invalid webhook payload: missing data");
1322
+ }
1323
+ const validEvents = ["ticket.created", "ticket.updated", "ticket.comment.created"];
1324
+ if (!validEvents.includes(event.event)) {
1325
+ throw new Error(`Invalid webhook payload: unknown event type "${event.event}"`);
1326
+ }
1327
+ return event;
1328
+ }
1329
+
901
1330
  // src/utils/pagination.ts
902
1331
  async function collectAll(iterable) {
903
1332
  const items = [];
@@ -918,6 +1347,19 @@ exports.ServerError = ServerError;
918
1347
  exports.TimeoutError = TimeoutError;
919
1348
  exports.ValidationError = ValidationError;
920
1349
  exports.collectAll = collectAll;
1350
+ exports.isAuthenticationError = isAuthenticationError;
1351
+ exports.isCommentCreatedEvent = isCommentCreatedEvent;
1352
+ exports.isConflictError = isConflictError;
1353
+ exports.isDispatchTicketsError = isDispatchTicketsError;
1354
+ exports.isNetworkError = isNetworkError;
1355
+ exports.isNotFoundError = isNotFoundError;
1356
+ exports.isRateLimitError = isRateLimitError;
1357
+ exports.isServerError = isServerError;
1358
+ exports.isTicketCreatedEvent = isTicketCreatedEvent;
1359
+ exports.isTicketUpdatedEvent = isTicketUpdatedEvent;
1360
+ exports.isTimeoutError = isTimeoutError;
1361
+ exports.isValidationError = isValidationError;
1362
+ exports.parseWebhookEvent = parseWebhookEvent;
921
1363
  exports.webhookUtils = webhookUtils;
922
1364
  //# sourceMappingURL=index.cjs.map
923
1365
  //# sourceMappingURL=index.cjs.map