@dispatchtickets/sdk 0.1.0 → 0.3.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.d.ts CHANGED
@@ -1,9 +1,17 @@
1
+ /**
2
+ * Custom fetch function type
3
+ */
4
+ type FetchFunction = typeof fetch;
1
5
  interface HttpClientConfig {
2
6
  baseUrl: string;
3
7
  apiKey: string;
4
8
  timeout: number;
5
9
  maxRetries: number;
6
10
  debug?: boolean;
11
+ /**
12
+ * Custom fetch implementation for testing/mocking
13
+ */
14
+ fetch?: FetchFunction;
7
15
  }
8
16
  interface RequestOptions {
9
17
  method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
@@ -18,6 +26,7 @@ interface RequestOptions {
18
26
  */
19
27
  declare class HttpClient {
20
28
  private readonly config;
29
+ private readonly fetchFn;
21
30
  constructor(config: HttpClientConfig);
22
31
  /**
23
32
  * Execute an HTTP request with retry logic
@@ -47,6 +56,104 @@ declare abstract class BaseResource {
47
56
  protected _delete<T>(path: string, query?: RequestOptions['query']): Promise<T>;
48
57
  }
49
58
 
59
+ /**
60
+ * Account represents the organization/company using Dispatch Tickets
61
+ */
62
+ interface Account {
63
+ id: string;
64
+ stackbeCustomerId: string;
65
+ stackbeOrganizationId: string;
66
+ createdAt: string;
67
+ updatedAt: string;
68
+ }
69
+ /**
70
+ * Usage statistics for the account
71
+ */
72
+ interface AccountUsage {
73
+ ticketsThisMonth: number;
74
+ ticketsTotal: number;
75
+ brandsCount: number;
76
+ plan?: {
77
+ name: string;
78
+ ticketLimit: number;
79
+ brandLimit: number | null;
80
+ };
81
+ }
82
+ /**
83
+ * API key for programmatic access
84
+ */
85
+ interface ApiKey {
86
+ id: string;
87
+ name: string;
88
+ keyPreview: string;
89
+ allBrands: boolean;
90
+ brandIds: string[];
91
+ lastUsedAt: string | null;
92
+ createdAt: string;
93
+ }
94
+ /**
95
+ * API key with the full key value (only returned on creation)
96
+ */
97
+ interface ApiKeyWithSecret extends ApiKey {
98
+ key: string;
99
+ }
100
+ /**
101
+ * Input for creating an API key
102
+ */
103
+ interface CreateApiKeyInput {
104
+ name: string;
105
+ /**
106
+ * If true, the API key can access all brands (current and future)
107
+ */
108
+ allBrands?: boolean;
109
+ /**
110
+ * Specific brand IDs this key can access (ignored if allBrands is true)
111
+ */
112
+ brandIds?: string[];
113
+ }
114
+ /**
115
+ * Input for updating API key scope
116
+ */
117
+ interface UpdateApiKeyScopeInput {
118
+ allBrands?: boolean;
119
+ brandIds?: string[];
120
+ }
121
+
122
+ /**
123
+ * Accounts resource for managing the current account
124
+ */
125
+ declare class AccountsResource extends BaseResource {
126
+ /**
127
+ * Get the current account
128
+ */
129
+ me(): Promise<Account>;
130
+ /**
131
+ * Get usage statistics for the current account
132
+ */
133
+ getUsage(): Promise<AccountUsage>;
134
+ /**
135
+ * List all API keys for the current account
136
+ */
137
+ listApiKeys(): Promise<ApiKey[]>;
138
+ /**
139
+ * Create a new API key
140
+ *
141
+ * Note: The full key value is only returned once on creation.
142
+ * Store it securely as it cannot be retrieved again.
143
+ */
144
+ createApiKey(data: CreateApiKeyInput): Promise<ApiKeyWithSecret>;
145
+ /**
146
+ * Update the brand scope for an API key
147
+ */
148
+ updateApiKeyScope(keyId: string, data: UpdateApiKeyScopeInput): Promise<{
149
+ success: boolean;
150
+ }>;
151
+ /**
152
+ * Revoke an API key
153
+ */
154
+ revokeApiKey(keyId: string): Promise<void>;
155
+ }
156
+
50
157
  /**
51
158
  * Brand (workspace) resource
52
159
  */
@@ -144,6 +251,26 @@ declare class BrandsResource extends BaseResource {
144
251
  * Update the ticket schema for a brand
145
252
  */
146
253
  updateSchema(brandId: string, schema: Record<string, unknown>): Promise<Record<string, unknown>>;
254
+ /**
255
+ * Get the inbound email address for a brand
256
+ *
257
+ * Emails sent to this address will automatically create tickets.
258
+ *
259
+ * @param brandId - The brand ID
260
+ * @param domain - Optional custom inbound domain (default: inbound.dispatchtickets.com)
261
+ * @returns The inbound email address
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * const email = client.brands.getInboundEmail('br_abc123');
266
+ * // Returns: br_abc123@inbound.dispatchtickets.com
267
+ *
268
+ * // With custom domain:
269
+ * const customEmail = client.brands.getInboundEmail('br_abc123', 'support.mycompany.com');
270
+ * // Returns: br_abc123@support.mycompany.com
271
+ * ```
272
+ */
273
+ getInboundEmail(brandId: string, domain?: string): string;
147
274
  }
148
275
 
149
276
  /**
@@ -185,9 +312,10 @@ type AuthorType = 'CUSTOMER' | 'AGENT' | 'SYSTEM';
185
312
  */
186
313
  type AttachmentStatus = 'PENDING' | 'UPLOADED' | 'FAILED';
187
314
  /**
188
- * Webhook event types
315
+ * Webhook event type strings (for subscription)
316
+ * @deprecated Use WebhookEventType from events.ts for typed event handling
189
317
  */
190
- type WebhookEvent = 'ticket.created' | 'ticket.updated' | 'ticket.deleted' | 'comment.created' | 'comment.updated' | 'comment.deleted' | 'attachment.created' | 'attachment.deleted';
318
+ type WebhookEventName = 'ticket.created' | 'ticket.updated' | 'ticket.deleted' | 'ticket.comment.created' | 'comment.created' | 'comment.updated' | 'comment.deleted' | 'attachment.created' | 'attachment.deleted';
191
319
  /**
192
320
  * Custom field types
193
321
  */
@@ -655,7 +783,7 @@ interface Webhook {
655
783
  id: string;
656
784
  brandId: string;
657
785
  url: string;
658
- events: WebhookEvent[];
786
+ events: WebhookEventName[];
659
787
  enabled: boolean;
660
788
  failureCount: number;
661
789
  lastTriggered?: string;
@@ -669,7 +797,7 @@ interface Webhook {
669
797
  interface CreateWebhookInput {
670
798
  url: string;
671
799
  secret: string;
672
- events: WebhookEvent[];
800
+ events: WebhookEventName[];
673
801
  }
674
802
  /**
675
803
  * Webhook delivery record
@@ -677,7 +805,7 @@ interface CreateWebhookInput {
677
805
  interface WebhookDelivery {
678
806
  id: string;
679
807
  webhookId: string;
680
- event: WebhookEvent;
808
+ event: WebhookEventName;
681
809
  payload: Record<string, unknown>;
682
810
  status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'RETRYING';
683
811
  attempts: number;
@@ -922,6 +1050,10 @@ interface DispatchTicketsConfig {
922
1050
  * @default false
923
1051
  */
924
1052
  debug?: boolean;
1053
+ /**
1054
+ * Custom fetch implementation for testing/mocking
1055
+ */
1056
+ fetch?: FetchFunction;
925
1057
  }
926
1058
  /**
927
1059
  * Dispatch Tickets SDK client
@@ -951,6 +1083,10 @@ interface DispatchTicketsConfig {
951
1083
  */
952
1084
  declare class DispatchTickets {
953
1085
  private readonly http;
1086
+ /**
1087
+ * Accounts resource for managing the current account and API keys
1088
+ */
1089
+ readonly accounts: AccountsResource;
954
1090
  /**
955
1091
  * Brands (workspaces) resource
956
1092
  */
@@ -1065,6 +1201,136 @@ declare class NetworkError extends DispatchTicketsError {
1065
1201
  constructor(message?: string);
1066
1202
  }
1067
1203
 
1204
+ /**
1205
+ * All supported webhook event types
1206
+ */
1207
+ type WebhookEventType = 'ticket.created' | 'ticket.updated' | 'ticket.comment.created';
1208
+ /**
1209
+ * Base webhook event envelope
1210
+ */
1211
+ interface WebhookEventEnvelope<T extends WebhookEventType, D> {
1212
+ /** Unique event ID */
1213
+ id: string;
1214
+ /** Event type */
1215
+ event: T;
1216
+ /** Brand ID that triggered the event */
1217
+ brand_id: string;
1218
+ /** Event data */
1219
+ data: D;
1220
+ /** ISO timestamp when event was created */
1221
+ timestamp: string;
1222
+ }
1223
+ /**
1224
+ * Customer info included in events
1225
+ */
1226
+ interface EventCustomerInfo {
1227
+ customerId: string | null;
1228
+ customerEmail: string | null;
1229
+ customerName: string | null;
1230
+ }
1231
+ /**
1232
+ * Payload for ticket.created event
1233
+ */
1234
+ interface TicketCreatedData extends EventCustomerInfo {
1235
+ id: string;
1236
+ ticketNumber: number;
1237
+ title: string;
1238
+ status: TicketStatus;
1239
+ priority: TicketPriority;
1240
+ source: TicketSource;
1241
+ createdAt: string;
1242
+ }
1243
+ /**
1244
+ * Payload for ticket.updated event
1245
+ */
1246
+ interface TicketUpdatedData extends EventCustomerInfo {
1247
+ id: string;
1248
+ ticketNumber: number;
1249
+ title: string;
1250
+ status: TicketStatus;
1251
+ priority: TicketPriority;
1252
+ assigneeId: string | null;
1253
+ updatedAt: string;
1254
+ /** List of field names that were changed */
1255
+ changes: string[];
1256
+ }
1257
+ /**
1258
+ * Comment data in ticket.comment.created event
1259
+ */
1260
+ interface EventCommentData {
1261
+ id: string;
1262
+ body: string;
1263
+ authorId: string | null;
1264
+ authorType: AuthorType;
1265
+ createdAt: string;
1266
+ }
1267
+ /**
1268
+ * Payload for ticket.comment.created event
1269
+ */
1270
+ interface CommentCreatedData extends EventCustomerInfo {
1271
+ ticketId: string;
1272
+ /** Formatted ticket number (e.g., "ACME-123") */
1273
+ ticketNumber: string;
1274
+ comment: EventCommentData;
1275
+ }
1276
+ /**
1277
+ * Ticket created webhook event
1278
+ */
1279
+ type TicketCreatedEvent = WebhookEventEnvelope<'ticket.created', TicketCreatedData>;
1280
+ /**
1281
+ * Ticket updated webhook event
1282
+ */
1283
+ type TicketUpdatedEvent = WebhookEventEnvelope<'ticket.updated', TicketUpdatedData>;
1284
+ /**
1285
+ * Comment created webhook event
1286
+ */
1287
+ type CommentCreatedEvent = WebhookEventEnvelope<'ticket.comment.created', CommentCreatedData>;
1288
+ /**
1289
+ * Union of all webhook events
1290
+ */
1291
+ type WebhookEvent = TicketCreatedEvent | TicketUpdatedEvent | CommentCreatedEvent;
1292
+ /**
1293
+ * Map of event types to their data types
1294
+ */
1295
+ interface WebhookEventMap {
1296
+ 'ticket.created': TicketCreatedEvent;
1297
+ 'ticket.updated': TicketUpdatedEvent;
1298
+ 'ticket.comment.created': CommentCreatedEvent;
1299
+ }
1300
+ /**
1301
+ * Type guard to check if event is a ticket.created event
1302
+ */
1303
+ declare function isTicketCreatedEvent(event: WebhookEvent): event is TicketCreatedEvent;
1304
+ /**
1305
+ * Type guard to check if event is a ticket.updated event
1306
+ */
1307
+ declare function isTicketUpdatedEvent(event: WebhookEvent): event is TicketUpdatedEvent;
1308
+ /**
1309
+ * Type guard to check if event is a ticket.comment.created event
1310
+ */
1311
+ declare function isCommentCreatedEvent(event: WebhookEvent): event is CommentCreatedEvent;
1312
+ /**
1313
+ * Parse and validate a webhook payload
1314
+ *
1315
+ * @param payload - Raw JSON payload string or parsed object
1316
+ * @returns Typed webhook event
1317
+ * @throws Error if payload is invalid
1318
+ *
1319
+ * @example
1320
+ * ```typescript
1321
+ * import { parseWebhookEvent, isTicketCreatedEvent } from '@dispatchtickets/sdk';
1322
+ *
1323
+ * app.post('/webhooks', (req, res) => {
1324
+ * const event = parseWebhookEvent(req.body);
1325
+ *
1326
+ * if (isTicketCreatedEvent(event)) {
1327
+ * console.log('New ticket:', event.data.title);
1328
+ * }
1329
+ * });
1330
+ * ```
1331
+ */
1332
+ declare function parseWebhookEvent(payload: string | object): WebhookEvent;
1333
+
1068
1334
  /**
1069
1335
  * Webhook signature verification utilities
1070
1336
  */
@@ -1113,4 +1379,4 @@ declare const webhookUtils: {
1113
1379
  */
1114
1380
  declare function collectAll<T>(iterable: AsyncIterable<T>): Promise<T[]>;
1115
1381
 
1116
- export { type Attachment, type AttachmentStatus, type AttachmentWithUrl, AuthenticationError, type AuthorType, type Brand, type BulkAction, type BulkActionResult, type Category, type CategoryStats, type Comment, type Company, ConflictError, 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 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 TicketPriority, type TicketSource, type TicketStatus, TimeoutError, type UpdateBrandInput, type UpdateCategoryInput, type UpdateCommentInput, type UpdateCustomerInput, type UpdateFieldInput, type UpdateTagInput, type UpdateTicketInput, ValidationError, type Webhook, type WebhookDelivery, type WebhookEvent, collectAll, webhookUtils };
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 };
package/dist/index.js CHANGED
@@ -74,8 +74,10 @@ var NetworkError = class extends DispatchTicketsError {
74
74
  // src/utils/http.ts
75
75
  var HttpClient = class {
76
76
  config;
77
+ fetchFn;
77
78
  constructor(config) {
78
79
  this.config = config;
80
+ this.fetchFn = config.fetch ?? fetch;
79
81
  }
80
82
  /**
81
83
  * Execute an HTTP request with retry logic
@@ -155,7 +157,7 @@ var HttpClient = class {
155
157
  console.log("[DispatchTickets] Body:", JSON.stringify(body, null, 2));
156
158
  }
157
159
  }
158
- const response = await fetch(url, {
160
+ const response = await this.fetchFn(url, {
159
161
  method,
160
162
  headers,
161
163
  body: body ? JSON.stringify(body) : void 0,
@@ -268,6 +270,52 @@ var BaseResource = class {
268
270
  }
269
271
  };
270
272
 
273
+ // src/resources/accounts.ts
274
+ var AccountsResource = class extends BaseResource {
275
+ /**
276
+ * Get the current account
277
+ */
278
+ async me() {
279
+ return this._get("/accounts/me");
280
+ }
281
+ /**
282
+ * Get usage statistics for the current account
283
+ */
284
+ async getUsage() {
285
+ return this._get("/accounts/me/usage");
286
+ }
287
+ /**
288
+ * List all API keys for the current account
289
+ */
290
+ async listApiKeys() {
291
+ return this._get("/accounts/me/api-keys");
292
+ }
293
+ /**
294
+ * Create a new API key
295
+ *
296
+ * Note: The full key value is only returned once on creation.
297
+ * Store it securely as it cannot be retrieved again.
298
+ */
299
+ async createApiKey(data) {
300
+ return this._post("/accounts/me/api-keys", data);
301
+ }
302
+ /**
303
+ * Update the brand scope for an API key
304
+ */
305
+ async updateApiKeyScope(keyId, data) {
306
+ return this._patch(
307
+ `/accounts/me/api-keys/${keyId}/scope`,
308
+ data
309
+ );
310
+ }
311
+ /**
312
+ * Revoke an API key
313
+ */
314
+ async revokeApiKey(keyId) {
315
+ await this._delete(`/accounts/me/api-keys/${keyId}`);
316
+ }
317
+ };
318
+
271
319
  // src/resources/brands.ts
272
320
  var BrandsResource = class extends BaseResource {
273
321
  /**
@@ -318,6 +366,28 @@ var BrandsResource = class extends BaseResource {
318
366
  async updateSchema(brandId, schema) {
319
367
  return this._put(`/brands/${brandId}/schema`, schema);
320
368
  }
369
+ /**
370
+ * Get the inbound email address for a brand
371
+ *
372
+ * Emails sent to this address will automatically create tickets.
373
+ *
374
+ * @param brandId - The brand ID
375
+ * @param domain - Optional custom inbound domain (default: inbound.dispatchtickets.com)
376
+ * @returns The inbound email address
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * const email = client.brands.getInboundEmail('br_abc123');
381
+ * // Returns: br_abc123@inbound.dispatchtickets.com
382
+ *
383
+ * // With custom domain:
384
+ * const customEmail = client.brands.getInboundEmail('br_abc123', 'support.mycompany.com');
385
+ * // Returns: br_abc123@support.mycompany.com
386
+ * ```
387
+ */
388
+ getInboundEmail(brandId, domain = "inbound.dispatchtickets.com") {
389
+ return `${brandId}@${domain}`;
390
+ }
321
391
  };
322
392
 
323
393
  // src/resources/tickets.ts
@@ -832,6 +902,10 @@ var webhookUtils = {
832
902
  // src/client.ts
833
903
  var DispatchTickets = class {
834
904
  http;
905
+ /**
906
+ * Accounts resource for managing the current account and API keys
907
+ */
908
+ accounts;
835
909
  /**
836
910
  * Brands (workspaces) resource
837
911
  */
@@ -881,9 +955,11 @@ var DispatchTickets = class {
881
955
  apiKey: config.apiKey,
882
956
  timeout: config.timeout ?? 3e4,
883
957
  maxRetries: config.maxRetries ?? 3,
884
- debug: config.debug ?? false
958
+ debug: config.debug ?? false,
959
+ fetch: config.fetch
885
960
  };
886
961
  this.http = new HttpClient(httpConfig);
962
+ this.accounts = new AccountsResource(this.http);
887
963
  this.brands = new BrandsResource(this.http);
888
964
  this.tickets = new TicketsResource(this.http);
889
965
  this.comments = new CommentsResource(this.http);
@@ -896,6 +972,37 @@ var DispatchTickets = class {
896
972
  }
897
973
  };
898
974
 
975
+ // src/types/events.ts
976
+ function isTicketCreatedEvent(event) {
977
+ return event.event === "ticket.created";
978
+ }
979
+ function isTicketUpdatedEvent(event) {
980
+ return event.event === "ticket.updated";
981
+ }
982
+ function isCommentCreatedEvent(event) {
983
+ return event.event === "ticket.comment.created";
984
+ }
985
+ function parseWebhookEvent(payload) {
986
+ const event = typeof payload === "string" ? JSON.parse(payload) : payload;
987
+ if (!event || typeof event !== "object") {
988
+ throw new Error("Invalid webhook payload: expected object");
989
+ }
990
+ if (!event.event || typeof event.event !== "string") {
991
+ throw new Error("Invalid webhook payload: missing event type");
992
+ }
993
+ if (!event.id || typeof event.id !== "string") {
994
+ throw new Error("Invalid webhook payload: missing event id");
995
+ }
996
+ if (!event.data || typeof event.data !== "object") {
997
+ throw new Error("Invalid webhook payload: missing data");
998
+ }
999
+ const validEvents = ["ticket.created", "ticket.updated", "ticket.comment.created"];
1000
+ if (!validEvents.includes(event.event)) {
1001
+ throw new Error(`Invalid webhook payload: unknown event type "${event.event}"`);
1002
+ }
1003
+ return event;
1004
+ }
1005
+
899
1006
  // src/utils/pagination.ts
900
1007
  async function collectAll(iterable) {
901
1008
  const items = [];
@@ -905,6 +1012,6 @@ async function collectAll(iterable) {
905
1012
  return items;
906
1013
  }
907
1014
 
908
- export { AuthenticationError, ConflictError, DispatchTickets, DispatchTicketsError, NetworkError, NotFoundError, RateLimitError, ServerError, TimeoutError, ValidationError, collectAll, webhookUtils };
1015
+ export { AuthenticationError, ConflictError, DispatchTickets, DispatchTicketsError, NetworkError, NotFoundError, RateLimitError, ServerError, TimeoutError, ValidationError, collectAll, isCommentCreatedEvent, isTicketCreatedEvent, isTicketUpdatedEvent, parseWebhookEvent, webhookUtils };
909
1016
  //# sourceMappingURL=index.js.map
910
1017
  //# sourceMappingURL=index.js.map