@dispatchtickets/sdk 0.5.0 → 0.7.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.cts CHANGED
@@ -346,6 +346,8 @@ interface Brand {
346
346
  autoresponseBody?: string;
347
347
  fromName?: string;
348
348
  fromEmail?: string;
349
+ /** Allowed origins for portal API CORS */
350
+ portalOrigins: string[];
349
351
  createdAt: string;
350
352
  updatedAt: string;
351
353
  }
@@ -376,6 +378,8 @@ interface UpdateBrandInput {
376
378
  autoresponseBody?: string;
377
379
  metadata?: Record<string, unknown>;
378
380
  ticketSchema?: Record<string, unknown>;
381
+ /** Allowed origins for portal API CORS. Empty array = block all cross-origin requests. */
382
+ portalOrigins?: string[];
379
383
  }
380
384
  /**
381
385
  * Brand deletion preview result
@@ -387,6 +391,134 @@ interface DeleteBrandPreview {
387
391
  tagCount: number;
388
392
  }
389
393
 
394
+ /**
395
+ * Portal API Types
396
+ *
397
+ * Types for the customer-facing portal API.
398
+ * Used with the DispatchPortal client.
399
+ */
400
+ /**
401
+ * Response from portal token generation or verification
402
+ */
403
+ interface PortalTokenResponse {
404
+ /** JWT portal token for Authorization header */
405
+ token: string;
406
+ /** ISO 8601 expiration timestamp */
407
+ expiresAt: string;
408
+ /** Customer ID */
409
+ customerId: string;
410
+ /** Customer email */
411
+ email: string;
412
+ /** Customer name (if provided) */
413
+ name?: string;
414
+ }
415
+ /**
416
+ * Input for generating a portal token
417
+ */
418
+ interface GeneratePortalTokenInput {
419
+ /** Customer email address */
420
+ email: string;
421
+ /** Customer name (optional) */
422
+ name?: string;
423
+ }
424
+ /**
425
+ * Input for sending a magic link email
426
+ */
427
+ interface SendMagicLinkInput {
428
+ /** Customer email address */
429
+ email: string;
430
+ /** URL to redirect to after verification (your portal URL) */
431
+ portalUrl: string;
432
+ }
433
+ /**
434
+ * Ticket summary (returned in list views)
435
+ */
436
+ interface PortalTicket {
437
+ /** Ticket ID */
438
+ id: string;
439
+ /** Formatted ticket number (e.g., "TKT-1001") */
440
+ ticketNumber: string;
441
+ /** Ticket title */
442
+ title: string;
443
+ /** Current status */
444
+ status: string;
445
+ /** Priority level */
446
+ priority: string;
447
+ /** Number of comments on the ticket */
448
+ commentCount: number;
449
+ /** Creation timestamp */
450
+ createdAt: string;
451
+ /** Last update timestamp */
452
+ updatedAt: string;
453
+ }
454
+ /**
455
+ * Ticket detail with body and comments
456
+ */
457
+ interface PortalTicketDetail extends PortalTicket {
458
+ /** Ticket body/description */
459
+ body: string;
460
+ /** Brand information */
461
+ brand: {
462
+ /** Brand name */
463
+ name: string;
464
+ };
465
+ /** Comments on the ticket (excludes internal comments) */
466
+ comments: PortalComment[];
467
+ }
468
+ /**
469
+ * Comment on a ticket
470
+ */
471
+ interface PortalComment {
472
+ /** Comment ID */
473
+ id: string;
474
+ /** Comment body */
475
+ body: string;
476
+ /** Author type */
477
+ authorType: 'CUSTOMER' | 'STAFF';
478
+ /** Author display name */
479
+ authorName: string;
480
+ /** Creation timestamp */
481
+ createdAt: string;
482
+ }
483
+ /**
484
+ * Input for creating a ticket via portal
485
+ */
486
+ interface PortalCreateTicketInput {
487
+ /** Ticket title */
488
+ title: string;
489
+ /** Ticket body/description (optional) */
490
+ body?: string;
491
+ }
492
+ /**
493
+ * Filters for listing tickets
494
+ */
495
+ interface PortalListTicketsFilters {
496
+ /** Filter by status */
497
+ status?: string;
498
+ /** Sort field */
499
+ sort?: 'createdAt' | 'updatedAt';
500
+ /** Sort order */
501
+ order?: 'asc' | 'desc';
502
+ /** Maximum results per page (1-50, default 20) */
503
+ limit?: number;
504
+ /** Pagination cursor */
505
+ cursor?: string;
506
+ }
507
+ /**
508
+ * Paginated list of tickets
509
+ */
510
+ interface PortalTicketListResponse {
511
+ /** Array of tickets */
512
+ data: PortalTicket[];
513
+ /** Pagination info */
514
+ pagination: {
515
+ /** Whether more results are available */
516
+ hasMore: boolean;
517
+ /** Cursor for next page (null if no more) */
518
+ nextCursor: string | null;
519
+ };
520
+ }
521
+
390
522
  /**
391
523
  * Brands resource for managing workspaces
392
524
  */
@@ -441,6 +573,61 @@ declare class BrandsResource extends BaseResource {
441
573
  * ```
442
574
  */
443
575
  getInboundEmail(brandId: string, domain?: string): string;
576
+ /**
577
+ * Generate a portal token for a customer
578
+ *
579
+ * Use this for "authenticated mode" where your backend already knows who the
580
+ * customer is (they're logged into your app). Generate a portal token and
581
+ * pass it to your frontend to initialize the DispatchPortal client.
582
+ *
583
+ * @param brandId - The brand ID
584
+ * @param data - Customer email and optional name
585
+ * @returns Portal token response with JWT token and expiry
586
+ *
587
+ * @example
588
+ * ```typescript
589
+ * // On your backend (Node.js, Next.js API route, etc.)
590
+ * const { token, expiresAt } = await client.brands.generatePortalToken(
591
+ * 'br_abc123',
592
+ * {
593
+ * email: req.user.email,
594
+ * name: req.user.name,
595
+ * }
596
+ * );
597
+ *
598
+ * // Return token to your frontend
599
+ * res.json({ portalToken: token });
600
+ * ```
601
+ */
602
+ generatePortalToken(brandId: string, data: GeneratePortalTokenInput): Promise<PortalTokenResponse>;
603
+ /**
604
+ * Send a magic link email to a customer
605
+ *
606
+ * Use this for "self-auth mode" where customers access the portal without
607
+ * being logged into your app. They receive an email with a link that
608
+ * authenticates them directly.
609
+ *
610
+ * @param brandId - The brand ID
611
+ * @param data - Customer email and portal URL to redirect to
612
+ * @returns Success status
613
+ *
614
+ * @example
615
+ * ```typescript
616
+ * // Customer requests access via your portal login form
617
+ * await client.brands.sendPortalMagicLink('br_abc123', {
618
+ * email: 'customer@example.com',
619
+ * portalUrl: 'https://yourapp.com/support/portal',
620
+ * });
621
+ *
622
+ * // Customer receives email with link like:
623
+ * // https://yourapp.com/support/portal?token=xyz...
624
+ *
625
+ * // Your portal page then calls DispatchPortal.verify(token)
626
+ * ```
627
+ */
628
+ sendPortalMagicLink(brandId: string, data: SendMagicLinkInput): Promise<{
629
+ success: boolean;
630
+ }>;
444
631
  }
445
632
 
446
633
  /**
@@ -1579,6 +1766,289 @@ declare class DispatchTickets {
1579
1766
  constructor(config: DispatchTicketsConfig);
1580
1767
  }
1581
1768
 
1769
+ /**
1770
+ * Options for creating a ticket via portal
1771
+ */
1772
+ interface PortalCreateTicketOptions extends ApiRequestOptions {
1773
+ /** Idempotency key to prevent duplicate creation */
1774
+ idempotencyKey?: string;
1775
+ }
1776
+ /**
1777
+ * Options for adding a comment via portal
1778
+ */
1779
+ interface PortalAddCommentOptions extends ApiRequestOptions {
1780
+ /** Idempotency key to prevent duplicate creation */
1781
+ idempotencyKey?: string;
1782
+ }
1783
+ /**
1784
+ * Portal tickets resource
1785
+ *
1786
+ * Provides methods for customers to manage their tickets via the portal API.
1787
+ *
1788
+ * @example
1789
+ * ```typescript
1790
+ * const portal = new DispatchPortal({ token: 'portal_token...' });
1791
+ *
1792
+ * // List tickets
1793
+ * const { data: tickets } = await portal.tickets.list();
1794
+ *
1795
+ * // Create a ticket
1796
+ * const ticket = await portal.tickets.create({
1797
+ * title: 'Need help with my order',
1798
+ * body: 'Order #12345 has not arrived...',
1799
+ * });
1800
+ *
1801
+ * // Add a comment
1802
+ * await portal.tickets.addComment(ticket.id, 'Any update on this?');
1803
+ * ```
1804
+ */
1805
+ declare class PortalTicketsResource extends BaseResource {
1806
+ /**
1807
+ * List the customer's tickets
1808
+ *
1809
+ * Returns a paginated list of tickets belonging to the authenticated customer.
1810
+ *
1811
+ * @param filters - Optional filters and pagination
1812
+ * @param options - Request options
1813
+ * @returns Paginated ticket list
1814
+ *
1815
+ * @example
1816
+ * ```typescript
1817
+ * // List all tickets
1818
+ * const { data: tickets, pagination } = await portal.tickets.list();
1819
+ *
1820
+ * // Filter by status
1821
+ * const openTickets = await portal.tickets.list({ status: 'open' });
1822
+ *
1823
+ * // Paginate
1824
+ * const page2 = await portal.tickets.list({ cursor: pagination.nextCursor });
1825
+ * ```
1826
+ */
1827
+ list(filters?: PortalListTicketsFilters, options?: ApiRequestOptions): Promise<PortalTicketListResponse>;
1828
+ /**
1829
+ * Iterate through all tickets with automatic pagination
1830
+ *
1831
+ * @param filters - Optional filters (cursor is managed automatically)
1832
+ * @returns Async iterator of tickets
1833
+ *
1834
+ * @example
1835
+ * ```typescript
1836
+ * for await (const ticket of portal.tickets.listAll({ status: 'open' })) {
1837
+ * console.log(ticket.title);
1838
+ * }
1839
+ * ```
1840
+ */
1841
+ listAll(filters?: Omit<PortalListTicketsFilters, 'cursor'>): AsyncIterable<PortalTicket>;
1842
+ /**
1843
+ * Get a ticket by ID
1844
+ *
1845
+ * Returns the ticket with its comments (excludes internal comments).
1846
+ *
1847
+ * @param ticketId - Ticket ID
1848
+ * @param options - Request options
1849
+ * @returns Ticket detail with comments
1850
+ *
1851
+ * @example
1852
+ * ```typescript
1853
+ * const ticket = await portal.tickets.get('tkt_abc123');
1854
+ * console.log(ticket.title);
1855
+ * console.log(`${ticket.comments.length} comments`);
1856
+ * ```
1857
+ */
1858
+ get(ticketId: string, options?: ApiRequestOptions): Promise<PortalTicketDetail>;
1859
+ /**
1860
+ * Create a new ticket
1861
+ *
1862
+ * @param data - Ticket data
1863
+ * @param options - Request options including idempotency key
1864
+ * @returns Created ticket
1865
+ *
1866
+ * @example
1867
+ * ```typescript
1868
+ * const ticket = await portal.tickets.create({
1869
+ * title: 'Need help with billing',
1870
+ * body: 'I was charged twice for my subscription...',
1871
+ * });
1872
+ * ```
1873
+ */
1874
+ create(data: PortalCreateTicketInput, options?: PortalCreateTicketOptions): Promise<PortalTicket>;
1875
+ /**
1876
+ * Add a comment to a ticket
1877
+ *
1878
+ * @param ticketId - Ticket ID
1879
+ * @param body - Comment text
1880
+ * @param options - Request options including idempotency key
1881
+ * @returns Created comment
1882
+ *
1883
+ * @example
1884
+ * ```typescript
1885
+ * const comment = await portal.tickets.addComment(
1886
+ * 'tkt_abc123',
1887
+ * 'Here is the additional information you requested...'
1888
+ * );
1889
+ * ```
1890
+ */
1891
+ addComment(ticketId: string, body: string, options?: PortalAddCommentOptions): Promise<PortalComment>;
1892
+ /**
1893
+ * Build query parameters from filters
1894
+ */
1895
+ private buildListQuery;
1896
+ }
1897
+
1898
+ /**
1899
+ * Configuration options for the Dispatch Portal client
1900
+ *
1901
+ * @example
1902
+ * ```typescript
1903
+ * const portal = new DispatchPortal({
1904
+ * token: 'portal_token_from_backend...',
1905
+ * });
1906
+ * ```
1907
+ */
1908
+ interface DispatchPortalConfig {
1909
+ /**
1910
+ * Portal token (required)
1911
+ *
1912
+ * Obtain this token by:
1913
+ * 1. Your backend calls `client.brands.generatePortalToken()` with the customer's email
1914
+ * 2. Pass the token to your frontend
1915
+ * 3. Frontend creates DispatchPortal with this token
1916
+ *
1917
+ * Or for self-auth mode:
1918
+ * 1. Customer clicks magic link email
1919
+ * 2. Your portal page calls `DispatchPortal.verify()` with the URL token
1920
+ * 3. Use the returned token to create DispatchPortal
1921
+ */
1922
+ token: string;
1923
+ /**
1924
+ * Base URL for the API
1925
+ * @default 'https://dispatch-tickets-api.onrender.com/v1'
1926
+ */
1927
+ baseUrl?: string;
1928
+ /**
1929
+ * Request timeout in milliseconds
1930
+ * @default 30000
1931
+ */
1932
+ timeout?: number;
1933
+ /**
1934
+ * Custom fetch implementation (for testing)
1935
+ */
1936
+ fetch?: FetchFunction;
1937
+ }
1938
+ /**
1939
+ * Dispatch Portal SDK client
1940
+ *
1941
+ * Customer-facing client for interacting with the portal API.
1942
+ * Use this to let customers view and manage their own tickets.
1943
+ *
1944
+ * @example Authenticated mode (backend generates token)
1945
+ * ```typescript
1946
+ * // On your backend:
1947
+ * const client = new DispatchTickets({ apiKey: 'sk_live_...' });
1948
+ * const { token } = await client.brands.generatePortalToken('br_abc123', {
1949
+ * email: 'customer@example.com',
1950
+ * name: 'Jane Doe',
1951
+ * });
1952
+ * // Pass token to frontend...
1953
+ *
1954
+ * // On your frontend:
1955
+ * const portal = new DispatchPortal({ token });
1956
+ * const { data: tickets } = await portal.tickets.list();
1957
+ * ```
1958
+ *
1959
+ * @example Self-auth mode (magic link)
1960
+ * ```typescript
1961
+ * // Customer clicks magic link, lands on your portal with ?token=xyz
1962
+ * const urlToken = new URLSearchParams(window.location.search).get('token');
1963
+ *
1964
+ * // Verify the magic link token
1965
+ * const { token } = await DispatchPortal.verify(urlToken);
1966
+ *
1967
+ * // Now use the portal token
1968
+ * const portal = new DispatchPortal({ token });
1969
+ * const { data: tickets } = await portal.tickets.list();
1970
+ * ```
1971
+ */
1972
+ declare class DispatchPortal {
1973
+ private readonly http;
1974
+ private readonly config;
1975
+ /**
1976
+ * Tickets resource for viewing and creating tickets
1977
+ *
1978
+ * @example
1979
+ * ```typescript
1980
+ * // List tickets
1981
+ * const { data: tickets } = await portal.tickets.list();
1982
+ *
1983
+ * // Create a ticket
1984
+ * const ticket = await portal.tickets.create({
1985
+ * title: 'Need help',
1986
+ * body: 'Something is broken...',
1987
+ * });
1988
+ *
1989
+ * // Add a comment
1990
+ * await portal.tickets.addComment(ticket.id, 'Here is more info...');
1991
+ * ```
1992
+ */
1993
+ readonly tickets: PortalTicketsResource;
1994
+ /**
1995
+ * Create a new Dispatch Portal client
1996
+ *
1997
+ * @param config - Portal client configuration
1998
+ * @throws Error if token is not provided
1999
+ */
2000
+ constructor(config: DispatchPortalConfig);
2001
+ /**
2002
+ * Verify a magic link token and get a portal token
2003
+ *
2004
+ * Call this when the customer clicks the magic link and lands on your portal.
2005
+ * The magic link contains a short-lived token that can be exchanged for a
2006
+ * longer-lived portal token.
2007
+ *
2008
+ * @param magicLinkToken - The token from the magic link URL
2009
+ * @param baseUrl - Optional API base URL
2010
+ * @param fetchFn - Optional custom fetch function (for testing)
2011
+ * @returns Portal token response
2012
+ *
2013
+ * @example
2014
+ * ```typescript
2015
+ * // Customer lands on: https://yourapp.com/portal?token=xyz
2016
+ * const urlToken = new URLSearchParams(window.location.search).get('token');
2017
+ *
2018
+ * const { token, email, name } = await DispatchPortal.verify(urlToken);
2019
+ *
2020
+ * // Store token and create client
2021
+ * localStorage.setItem('portalToken', token);
2022
+ * const portal = new DispatchPortal({ token });
2023
+ * ```
2024
+ */
2025
+ static verify(magicLinkToken: string, baseUrl?: string, fetchFn?: FetchFunction): Promise<PortalTokenResponse>;
2026
+ /**
2027
+ * Refresh the current portal token
2028
+ *
2029
+ * Call this before the token expires to get a new token with extended expiry.
2030
+ * The new token will have the same customer context.
2031
+ *
2032
+ * @returns New portal token response
2033
+ *
2034
+ * @example
2035
+ * ```typescript
2036
+ * // Check if token is close to expiry and refresh
2037
+ * const { token: newToken, expiresAt } = await portal.refresh();
2038
+ *
2039
+ * // Create new client with refreshed token
2040
+ * const newPortal = new DispatchPortal({ token: newToken });
2041
+ * ```
2042
+ */
2043
+ refresh(): Promise<PortalTokenResponse>;
2044
+ /**
2045
+ * Get the current token
2046
+ *
2047
+ * Useful for storing/passing the token.
2048
+ */
2049
+ get token(): string;
2050
+ }
2051
+
1582
2052
  /**
1583
2053
  * Base error class for all Dispatch Tickets SDK errors
1584
2054
  */
@@ -1873,4 +2343,4 @@ declare const webhookUtils: {
1873
2343
  */
1874
2344
  declare function collectAll<T>(iterable: AsyncIterable<T>): Promise<T[]>;
1875
2345
 
1876
- 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, DispatchTickets, type DispatchTicketsConfig, DispatchTicketsError, type EntityType, type EventCommentData, type EventCustomerInfo, type FieldDefinition, type FieldDefinitions, type FieldType, type Hooks, type InitiateUploadInput, type InitiateUploadResponse, type Link, type ListCustomersFilters, type ListTicketsFilters, type MergeTagsInput, type MergeTicketsInput, NetworkError, NotFoundError, type PaginatedResponse, RateLimitError, type RateLimitInfo, type RequestContext, type ResponseContext, type RetryConfig, 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 };
2346
+ 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 };