@dispatchtickets/sdk 0.5.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/dist/index.d.cts CHANGED
@@ -387,6 +387,134 @@ interface DeleteBrandPreview {
387
387
  tagCount: number;
388
388
  }
389
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
+
390
518
  /**
391
519
  * Brands resource for managing workspaces
392
520
  */
@@ -441,6 +569,61 @@ declare class BrandsResource extends BaseResource {
441
569
  * ```
442
570
  */
443
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
+ }>;
444
627
  }
445
628
 
446
629
  /**
@@ -1579,6 +1762,289 @@ declare class DispatchTickets {
1579
1762
  constructor(config: DispatchTicketsConfig);
1580
1763
  }
1581
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
+
1582
2048
  /**
1583
2049
  * Base error class for all Dispatch Tickets SDK errors
1584
2050
  */
@@ -1873,4 +2339,4 @@ declare const webhookUtils: {
1873
2339
  */
1874
2340
  declare function collectAll<T>(iterable: AsyncIterable<T>): Promise<T[]>;
1875
2341
 
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 };
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 };