@agentuity/core 1.0.29 → 1.0.30

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.
@@ -3,79 +3,281 @@ import { buildUrl, toServiceException } from './_util.ts';
3
3
  import { safeStringify } from '../json.ts';
4
4
 
5
5
  /**
6
- * An email address registered with the Agentuity email service
6
+ * An email address registered with the Agentuity email service.
7
+ *
8
+ * Email addresses are created under the `@agentuity.email` domain and can receive
9
+ * inbound emails (forwarded to configured destinations) and send outbound emails.
7
10
  */
8
11
  export interface EmailAddress {
12
+ /**
13
+ * Unique identifier for the email address.
14
+ *
15
+ * @remarks Prefixed with `eaddr_`.
16
+ */
9
17
  id: string;
18
+
19
+ /**
20
+ * The full email address (e.g., `support@agentuity.email`).
21
+ */
10
22
  email: string;
23
+
24
+ /**
25
+ * Provider-specific configuration (e.g., inbound routing config).
26
+ *
27
+ * @remarks Opaque to callers — the structure is managed by the platform.
28
+ */
11
29
  config?: Record<string, unknown>;
30
+
31
+ /**
32
+ * ID of the user who registered this address.
33
+ */
12
34
  created_by?: string;
35
+
36
+ /**
37
+ * ISO 8601 timestamp when the address was created.
38
+ */
13
39
  created_at: string;
40
+
41
+ /**
42
+ * ISO 8601 timestamp when the address was last updated.
43
+ */
14
44
  updated_at?: string;
45
+
46
+ /**
47
+ * Total number of inbound emails received at this address.
48
+ */
15
49
  inbound_count?: number;
50
+
51
+ /**
52
+ * Total number of outbound emails sent from this address.
53
+ */
16
54
  outbound_count?: number;
55
+
56
+ /**
57
+ * ISO 8601 timestamp of the most recent inbound or outbound email activity.
58
+ */
17
59
  last_activity?: string;
18
60
  }
19
61
 
20
62
  /**
21
- * A destination configuration for an email address
63
+ * A destination configuration for an email address.
64
+ *
65
+ * When an inbound email is received at the parent address, the platform forwards
66
+ * it to each configured destination via an HTTP request.
22
67
  */
23
68
  export interface EmailDestination {
69
+ /**
70
+ * Unique identifier for the destination.
71
+ *
72
+ * @remarks Prefixed with `edst_`.
73
+ */
24
74
  id: string;
75
+
76
+ /**
77
+ * The destination type. Currently only `'url'` is supported.
78
+ */
25
79
  type: string;
80
+
81
+ /**
82
+ * Destination-specific configuration.
83
+ *
84
+ * @remarks
85
+ * For `'url'` type the shape is:
86
+ * ```typescript
87
+ * {
88
+ * url: string; // Must use http or https; must not point to private/loopback addresses
89
+ * headers?: Record<string, string>;
90
+ * method?: 'POST' | 'PUT' | 'PATCH';
91
+ * }
92
+ * ```
93
+ */
26
94
  config?: Record<string, unknown>;
95
+
96
+ /**
97
+ * ISO 8601 timestamp when the destination was created.
98
+ */
27
99
  created_at: string;
100
+
101
+ /**
102
+ * ISO 8601 timestamp when the destination was last updated.
103
+ */
28
104
  updated_at?: string;
29
105
  }
30
106
 
107
+ /**
108
+ * Connection settings for an email protocol (IMAP or POP3).
109
+ *
110
+ * Used to configure a mail client for accessing an Agentuity email address
111
+ * via standard mail protocols.
112
+ */
31
113
  export interface EmailProtocolConfig {
114
+ /**
115
+ * The mail server hostname.
116
+ */
32
117
  host: string;
118
+
119
+ /**
120
+ * The mail server port number.
121
+ */
33
122
  port: number;
123
+
124
+ /**
125
+ * TLS mode (e.g., `'starttls'`, `'ssl'`, `'none'`).
126
+ */
34
127
  tls: string;
128
+
129
+ /**
130
+ * The authentication username (typically the address ID).
131
+ */
35
132
  username: string;
133
+
134
+ /**
135
+ * The authentication password.
136
+ */
36
137
  password: string;
37
138
  }
38
139
 
140
+ /**
141
+ * Full connection configuration for accessing an email address via IMAP and POP3 protocols.
142
+ *
143
+ * Returned by {@link EmailService.getConnectionConfig} to allow external mail clients
144
+ * to connect to an Agentuity email address.
145
+ */
39
146
  export interface EmailConnectionConfig {
147
+ /**
148
+ * The full email address these settings are for.
149
+ */
40
150
  email: string;
151
+
152
+ /**
153
+ * IMAP protocol connection settings.
154
+ */
41
155
  imap: EmailProtocolConfig;
156
+
157
+ /**
158
+ * POP3 protocol connection settings.
159
+ */
42
160
  pop3: EmailProtocolConfig;
43
161
  }
44
162
 
45
163
  /**
46
- * An inbound email message
164
+ * An inbound email message received at an Agentuity email address.
47
165
  */
48
166
  export interface EmailInbound {
167
+ /**
168
+ * Unique identifier for the inbound email.
169
+ *
170
+ * @remarks Prefixed with `einb_`.
171
+ */
49
172
  id: string;
173
+
174
+ /**
175
+ * The sender's email address.
176
+ */
50
177
  from: string;
178
+
179
+ /**
180
+ * The recipient email address (comma-separated if multiple).
181
+ */
51
182
  to: string;
183
+
184
+ /**
185
+ * The email subject line.
186
+ */
52
187
  subject?: string;
188
+
189
+ /**
190
+ * Plain text body of the email.
191
+ */
53
192
  text?: string;
193
+
194
+ /**
195
+ * HTML body of the email.
196
+ */
54
197
  html?: string;
198
+
199
+ /**
200
+ * ISO 8601 timestamp when the email was received.
201
+ */
55
202
  received_at?: string;
203
+
204
+ /**
205
+ * Raw email headers as key-value pairs.
206
+ */
56
207
  headers?: Record<string, unknown>;
208
+
209
+ /**
210
+ * Array of stored attachment metadata with S3 locations.
211
+ */
57
212
  attachments?: EmailStoredAttachment[];
58
213
  }
59
214
 
60
215
  /**
61
- * An outbound email message
216
+ * An outbound email message sent from an Agentuity email address.
62
217
  */
63
218
  export interface EmailOutbound {
219
+ /**
220
+ * Unique identifier for the outbound email.
221
+ *
222
+ * @remarks Prefixed with `eout_`.
223
+ */
64
224
  id: string;
225
+
226
+ /**
227
+ * The sender's email address (must be owned by the organization).
228
+ */
65
229
  from: string;
230
+
231
+ /**
232
+ * The recipient email addresses (comma-separated).
233
+ */
66
234
  to: string;
235
+
236
+ /**
237
+ * The email subject line.
238
+ */
67
239
  subject?: string;
240
+
241
+ /**
242
+ * Plain text body of the email.
243
+ */
68
244
  text?: string;
245
+
246
+ /**
247
+ * HTML body of the email.
248
+ */
69
249
  html?: string;
250
+
251
+ /**
252
+ * Delivery status: `'pending'`, `'success'`, or `'failed'`.
253
+ *
254
+ * @remarks Emails are sent asynchronously, so the initial status is always `'pending'`.
255
+ */
70
256
  status?: string;
257
+
258
+ /**
259
+ * Error message if the delivery failed.
260
+ */
71
261
  error?: string;
262
+
263
+ /**
264
+ * ISO 8601 timestamp when the send was initiated.
265
+ */
72
266
  created_at?: string;
267
+
268
+ /**
269
+ * Custom email headers that were included.
270
+ */
73
271
  headers?: Record<string, unknown>;
272
+
273
+ /**
274
+ * Array of stored attachment metadata with S3 locations.
275
+ */
74
276
  attachments?: EmailStoredAttachment[];
75
277
  }
76
278
 
77
279
  /**
78
- * An email attachment
280
+ * An email attachment to include when sending an outbound email.
79
281
  */
80
282
  export interface EmailAttachment {
81
283
  /**
@@ -154,26 +356,51 @@ export interface EmailSendParams {
154
356
  }
155
357
 
156
358
  /**
157
- * Parameters for email activity time-series
359
+ * Parameters for querying email activity time-series data.
158
360
  */
159
361
  export interface EmailActivityParams {
362
+ /**
363
+ * Number of days of activity to retrieve.
364
+ *
365
+ * @remarks Values below 7 are clamped to 7; values above 365 are clamped to 365.
366
+ *
367
+ * @default 7
368
+ */
160
369
  days?: number; // min 7, max 365, default 7
161
370
  }
162
371
 
163
372
  /**
164
- * A single data point in the email activity time-series
373
+ * A single data point in the email activity time-series.
165
374
  */
166
375
  export interface EmailActivityDataPoint {
376
+ /**
377
+ * The date in `YYYY-MM-DD` format.
378
+ */
167
379
  date: string; // "2026-02-28"
380
+
381
+ /**
382
+ * Number of inbound emails received on this date.
383
+ */
168
384
  inbound: number;
385
+
386
+ /**
387
+ * Number of outbound emails sent on this date.
388
+ */
169
389
  outbound: number;
170
390
  }
171
391
 
172
392
  /**
173
- * Result of email activity query
393
+ * Result of an email activity query containing daily time-series data.
174
394
  */
175
395
  export interface EmailActivityResult {
396
+ /**
397
+ * Array of daily activity data points, ordered chronologically.
398
+ */
176
399
  activity: EmailActivityDataPoint[];
400
+
401
+ /**
402
+ * The number of days of data returned.
403
+ */
177
404
  days: number;
178
405
  }
179
406
 
@@ -424,7 +651,20 @@ export interface EmailService {
424
651
 
425
652
  /**
426
653
  * Unwrap a Catalyst API response payload.
427
- * Handles both `{ key: data }` and `{ data: { key: data } }` response formats.
654
+ *
655
+ * The Catalyst API may return data in one of two envelope formats:
656
+ * - `{ key: data }` — the key maps directly to the data
657
+ * - `{ data: { key: data } }` — the data is nested inside a `data` wrapper
658
+ *
659
+ * This helper normalises both shapes so callers always receive the inner value.
660
+ *
661
+ * @param payload - The raw JSON-parsed response body from the API
662
+ * @param key - The property name to extract from the payload (e.g., `'address'`, `'destinations'`)
663
+ * @returns The extracted value cast to type `T`
664
+ *
665
+ * @remarks
666
+ * If neither envelope format matches, the raw payload is returned as-is.
667
+ * This function does not throw — it always returns a value.
428
668
  */
429
669
  function unwrap<T>(payload: unknown, key: string): T {
430
670
  if (typeof payload === 'object' && payload !== null) {
@@ -445,15 +685,63 @@ function unwrap<T>(payload: unknown, key: string): T {
445
685
 
446
686
  const EMAIL_ACTIVITY_API_VERSION = '2026-02-28';
447
687
 
688
+ /**
689
+ * Client for the Agentuity Email service.
690
+ *
691
+ * Provides methods for managing email addresses, configuring inbound email
692
+ * destinations, sending outbound emails, and querying email history.
693
+ *
694
+ * Email addresses are created under the `@agentuity.email` domain. Inbound emails
695
+ * can be forwarded to URL destinations. Outbound emails are sent asynchronously
696
+ * and support attachments up to 25 MB total.
697
+ *
698
+ * All methods are instrumented with OpenTelemetry spans for observability.
699
+ *
700
+ * @example
701
+ * ```typescript
702
+ * const email = new EmailStorageService(baseUrl, adapter);
703
+ *
704
+ * // Create an address
705
+ * const addr = await email.createAddress('notifications');
706
+ *
707
+ * // Send an email
708
+ * await email.send({
709
+ * from: addr.email,
710
+ * to: ['user@example.com'],
711
+ * subject: 'Hello',
712
+ * text: 'Hello from Agentuity!',
713
+ * });
714
+ * ```
715
+ */
448
716
  export class EmailStorageService implements EmailService {
449
717
  #adapter: FetchAdapter;
450
718
  #baseUrl: string;
451
719
 
720
+ /**
721
+ * Create a new EmailStorageService instance.
722
+ *
723
+ * @param baseUrl - The base URL for the Agentuity Email API (e.g., `https://api.agentuity.com`)
724
+ * @param adapter - The HTTP fetch adapter used for making API requests
725
+ */
452
726
  constructor(baseUrl: string, adapter: FetchAdapter) {
453
727
  this.#adapter = adapter;
454
728
  this.#baseUrl = baseUrl;
455
729
  }
456
730
 
731
+ /**
732
+ * Create a new email address under the `@agentuity.email` domain.
733
+ *
734
+ * @param localPart - The local part of the email address (the part before the `@`).
735
+ * For example, passing `'support'` creates `support@agentuity.email`.
736
+ * @returns The newly created email address record
737
+ * @throws ServiceException on API errors (e.g., duplicate address, invalid local part)
738
+ *
739
+ * @example
740
+ * ```typescript
741
+ * const addr = await email.createAddress('support');
742
+ * console.log('Created:', addr.email); // support@agentuity.email
743
+ * ```
744
+ */
457
745
  async createAddress(localPart: string): Promise<EmailAddress> {
458
746
  const url = buildUrl(this.#baseUrl, '/email/2025-03-17/addresses');
459
747
  const signal = AbortSignal.timeout(30_000);
@@ -475,6 +763,20 @@ export class EmailStorageService implements EmailService {
475
763
  throw await toServiceException('POST', url, res.response);
476
764
  }
477
765
 
766
+ /**
767
+ * List all email addresses owned by the current organization.
768
+ *
769
+ * @returns An array of email address records. Returns an empty array if none exist.
770
+ * @throws ServiceException on API errors
771
+ *
772
+ * @example
773
+ * ```typescript
774
+ * const addresses = await email.listAddresses();
775
+ * for (const addr of addresses) {
776
+ * console.log(`${addr.email} — ${addr.inbound_count ?? 0} received`);
777
+ * }
778
+ * ```
779
+ */
478
780
  async listAddresses(): Promise<EmailAddress[]> {
479
781
  const url = buildUrl(this.#baseUrl, '/email/2025-03-17/addresses');
480
782
  const signal = AbortSignal.timeout(30_000);
@@ -496,6 +798,21 @@ export class EmailStorageService implements EmailService {
496
798
  throw await toServiceException('GET', url, res.response);
497
799
  }
498
800
 
801
+ /**
802
+ * Get an email address by its ID.
803
+ *
804
+ * @param id - The email address ID (prefixed with `eaddr_`)
805
+ * @returns The email address record, or `null` if no address with the given ID exists
806
+ * @throws ServiceException on API errors (other than 404)
807
+ *
808
+ * @example
809
+ * ```typescript
810
+ * const addr = await email.getAddress('eaddr_abc123');
811
+ * if (addr) {
812
+ * console.log('Found:', addr.email);
813
+ * }
814
+ * ```
815
+ */
499
816
  async getAddress(id: string): Promise<EmailAddress | null> {
500
817
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/addresses/${encodeURIComponent(id)}`);
501
818
  const signal = AbortSignal.timeout(30_000);
@@ -518,6 +835,25 @@ export class EmailStorageService implements EmailService {
518
835
  throw await toServiceException('GET', url, res.response);
519
836
  }
520
837
 
838
+ /**
839
+ * Get IMAP and POP3 connection settings for an email address.
840
+ *
841
+ * These settings can be used to configure an external mail client (e.g., Thunderbird, Outlook)
842
+ * to access the mailbox associated with the given address.
843
+ *
844
+ * @param id - The email address ID (prefixed with `eaddr_`)
845
+ * @returns The connection configuration with IMAP and POP3 settings, or `null` if the address is not found
846
+ * @throws ServiceException on API errors (other than 404)
847
+ *
848
+ * @example
849
+ * ```typescript
850
+ * const config = await email.getConnectionConfig('eaddr_abc123');
851
+ * if (config) {
852
+ * console.log('IMAP host:', config.imap.host);
853
+ * console.log('POP3 host:', config.pop3.host);
854
+ * }
855
+ * ```
856
+ */
521
857
  async getConnectionConfig(id: string): Promise<EmailConnectionConfig | null> {
522
858
  const url = buildUrl(
523
859
  this.#baseUrl,
@@ -543,6 +879,19 @@ export class EmailStorageService implements EmailService {
543
879
  throw await toServiceException('GET', url, res.response);
544
880
  }
545
881
 
882
+ /**
883
+ * Delete an email address and all associated destinations.
884
+ *
885
+ * @remarks This operation is idempotent — deleting a non-existent address does not throw.
886
+ *
887
+ * @param id - The email address ID (prefixed with `eaddr_`)
888
+ * @throws ServiceException on API errors (other than 404)
889
+ *
890
+ * @example
891
+ * ```typescript
892
+ * await email.deleteAddress('eaddr_abc123');
893
+ * ```
894
+ */
546
895
  async deleteAddress(id: string): Promise<void> {
547
896
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/addresses/${encodeURIComponent(id)}`);
548
897
  const signal = AbortSignal.timeout(30_000);
@@ -562,6 +911,28 @@ export class EmailStorageService implements EmailService {
562
911
  throw await toServiceException('DELETE', url, res.response);
563
912
  }
564
913
 
914
+ /**
915
+ * Create a new destination for an email address.
916
+ *
917
+ * Destinations determine where inbound emails are forwarded when they arrive
918
+ * at the parent address.
919
+ *
920
+ * @param addressId - The email address ID (prefixed with `eaddr_`)
921
+ * @param type - The destination type (currently only `'url'` is supported)
922
+ * @param config - Type-specific destination configuration. For `'url'`:
923
+ * `{ url: string, headers?: Record<string, string>, method?: 'POST' | 'PUT' | 'PATCH' }`
924
+ * @returns The newly created destination record
925
+ * @throws ServiceException on API errors (e.g., invalid URL, address not found)
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * const dest = await email.createDestination('eaddr_abc123', 'url', {
930
+ * url: 'https://example.com/webhook',
931
+ * headers: { 'X-Secret': 'my-token' },
932
+ * });
933
+ * console.log('Destination created:', dest.id);
934
+ * ```
935
+ */
565
936
  async createDestination(
566
937
  addressId: string,
567
938
  type: string,
@@ -591,6 +962,21 @@ export class EmailStorageService implements EmailService {
591
962
  throw await toServiceException('POST', url, res.response);
592
963
  }
593
964
 
965
+ /**
966
+ * List all destinations configured for an email address.
967
+ *
968
+ * @param addressId - The email address ID (prefixed with `eaddr_`)
969
+ * @returns An array of destination records. Returns an empty array if none exist.
970
+ * @throws ServiceException on API errors
971
+ *
972
+ * @example
973
+ * ```typescript
974
+ * const destinations = await email.listDestinations('eaddr_abc123');
975
+ * for (const dest of destinations) {
976
+ * console.log(`${dest.type}: ${dest.id}`);
977
+ * }
978
+ * ```
979
+ */
594
980
  async listDestinations(addressId: string): Promise<EmailDestination[]> {
595
981
  const url = buildUrl(
596
982
  this.#baseUrl,
@@ -617,6 +1003,20 @@ export class EmailStorageService implements EmailService {
617
1003
  throw await toServiceException('GET', url, res.response);
618
1004
  }
619
1005
 
1006
+ /**
1007
+ * Delete a destination from an email address.
1008
+ *
1009
+ * @remarks This operation is idempotent — deleting a non-existent destination does not throw.
1010
+ *
1011
+ * @param addressId - The email address ID (prefixed with `eaddr_`)
1012
+ * @param destinationId - The destination ID (prefixed with `edst_`)
1013
+ * @throws ServiceException on API errors (other than 404)
1014
+ *
1015
+ * @example
1016
+ * ```typescript
1017
+ * await email.deleteDestination('eaddr_abc123', 'edst_xyz789');
1018
+ * ```
1019
+ */
620
1020
  async deleteDestination(addressId: string, destinationId: string): Promise<void> {
621
1021
  const url = buildUrl(
622
1022
  this.#baseUrl,
@@ -640,6 +1040,38 @@ export class EmailStorageService implements EmailService {
640
1040
  throw await toServiceException('DELETE', url, res.response);
641
1041
  }
642
1042
 
1043
+ /**
1044
+ * Send an outbound email from an Agentuity email address.
1045
+ *
1046
+ * Emails are sent asynchronously — this method returns immediately with an outbound
1047
+ * record whose status is `'pending'`. Use {@link getOutbound} to poll for delivery status.
1048
+ *
1049
+ * @remarks
1050
+ * - The `from` address must be owned by the current organization.
1051
+ * - Maximum 50 recipients per send.
1052
+ * - Maximum 25 MB for the full RFC 822 body (including attachments).
1053
+ *
1054
+ * @param params - The email send parameters including from, to, subject, and body
1055
+ * @returns The outbound email record with initial status `'pending'`
1056
+ * @throws ServiceException on API errors (e.g., invalid sender, too many recipients)
1057
+ *
1058
+ * @example
1059
+ * ```typescript
1060
+ * const result = await email.send({
1061
+ * from: 'notifications@agentuity.email',
1062
+ * to: ['user@example.com'],
1063
+ * subject: 'Welcome!',
1064
+ * text: 'Welcome to our platform.',
1065
+ * html: '<h1>Welcome!</h1>',
1066
+ * attachments: [{
1067
+ * filename: 'guide.pdf',
1068
+ * content: base64EncodedPdf,
1069
+ * contentType: 'application/pdf',
1070
+ * }],
1071
+ * });
1072
+ * console.log('Email queued:', result.id);
1073
+ * ```
1074
+ */
643
1075
  async send(params: EmailSendParams): Promise<EmailOutbound> {
644
1076
  const url = buildUrl(this.#baseUrl, '/email/2025-03-17/outbound/send');
645
1077
  const signal = AbortSignal.timeout(30_000);
@@ -686,6 +1118,26 @@ export class EmailStorageService implements EmailService {
686
1118
  throw await toServiceException('POST', url, res.response);
687
1119
  }
688
1120
 
1121
+ /**
1122
+ * List inbound emails, optionally filtered by email address.
1123
+ *
1124
+ * @param addressId - Optional email address ID (prefixed with `eaddr_`) to filter results.
1125
+ * When omitted, returns inbound emails across all addresses in the organization.
1126
+ * @returns An array of inbound email records. Returns an empty array if none exist.
1127
+ * @throws ServiceException on API errors
1128
+ *
1129
+ * @example
1130
+ * ```typescript
1131
+ * // List all inbound emails
1132
+ * const all = await email.listInbound();
1133
+ *
1134
+ * // List inbound for a specific address
1135
+ * const filtered = await email.listInbound('eaddr_abc123');
1136
+ * for (const msg of filtered) {
1137
+ * console.log(`From: ${msg.from}, Subject: ${msg.subject}`);
1138
+ * }
1139
+ * ```
1140
+ */
689
1141
  async listInbound(addressId?: string): Promise<EmailInbound[]> {
690
1142
  const queryParams = new URLSearchParams();
691
1143
  if (addressId) {
@@ -717,6 +1169,22 @@ export class EmailStorageService implements EmailService {
717
1169
  throw await toServiceException('GET', url, res.response);
718
1170
  }
719
1171
 
1172
+ /**
1173
+ * Get an inbound email by its ID.
1174
+ *
1175
+ * @param id - The inbound email ID (prefixed with `einb_`)
1176
+ * @returns The inbound email record, or `null` if not found
1177
+ * @throws ServiceException on API errors (other than 404)
1178
+ *
1179
+ * @example
1180
+ * ```typescript
1181
+ * const msg = await email.getInbound('einb_abc123');
1182
+ * if (msg) {
1183
+ * console.log('Subject:', msg.subject);
1184
+ * console.log('Attachments:', msg.attachments?.length ?? 0);
1185
+ * }
1186
+ * ```
1187
+ */
720
1188
  async getInbound(id: string): Promise<EmailInbound | null> {
721
1189
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/inbound/${encodeURIComponent(id)}`);
722
1190
  const signal = AbortSignal.timeout(30_000);
@@ -739,6 +1207,19 @@ export class EmailStorageService implements EmailService {
739
1207
  throw await toServiceException('GET', url, res.response);
740
1208
  }
741
1209
 
1210
+ /**
1211
+ * Delete an inbound email by its ID.
1212
+ *
1213
+ * @remarks This operation is idempotent — deleting a non-existent email does not throw.
1214
+ *
1215
+ * @param id - The inbound email ID (prefixed with `einb_`)
1216
+ * @throws ServiceException on API errors (other than 404)
1217
+ *
1218
+ * @example
1219
+ * ```typescript
1220
+ * await email.deleteInbound('einb_abc123');
1221
+ * ```
1222
+ */
742
1223
  async deleteInbound(id: string): Promise<void> {
743
1224
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/inbound/${encodeURIComponent(id)}`);
744
1225
  const signal = AbortSignal.timeout(30_000);
@@ -758,6 +1239,26 @@ export class EmailStorageService implements EmailService {
758
1239
  throw await toServiceException('DELETE', url, res.response);
759
1240
  }
760
1241
 
1242
+ /**
1243
+ * List outbound emails, optionally filtered by email address.
1244
+ *
1245
+ * @param addressId - Optional email address ID (prefixed with `eaddr_`) to filter results.
1246
+ * When omitted, returns outbound emails across all addresses in the organization.
1247
+ * @returns An array of outbound email records. Returns an empty array if none exist.
1248
+ * @throws ServiceException on API errors
1249
+ *
1250
+ * @example
1251
+ * ```typescript
1252
+ * // List all outbound emails
1253
+ * const all = await email.listOutbound();
1254
+ *
1255
+ * // List outbound for a specific address
1256
+ * const filtered = await email.listOutbound('eaddr_abc123');
1257
+ * for (const msg of filtered) {
1258
+ * console.log(`To: ${msg.to}, Status: ${msg.status}`);
1259
+ * }
1260
+ * ```
1261
+ */
761
1262
  async listOutbound(addressId?: string): Promise<EmailOutbound[]> {
762
1263
  const queryParams = new URLSearchParams();
763
1264
  if (addressId) {
@@ -789,6 +1290,24 @@ export class EmailStorageService implements EmailService {
789
1290
  throw await toServiceException('GET', url, res.response);
790
1291
  }
791
1292
 
1293
+ /**
1294
+ * Get an outbound email by its ID.
1295
+ *
1296
+ * @param id - The outbound email ID (prefixed with `eout_`)
1297
+ * @returns The outbound email record, or `null` if not found
1298
+ * @throws ServiceException on API errors (other than 404)
1299
+ *
1300
+ * @example
1301
+ * ```typescript
1302
+ * const msg = await email.getOutbound('eout_abc123');
1303
+ * if (msg) {
1304
+ * console.log('Status:', msg.status);
1305
+ * if (msg.error) {
1306
+ * console.error('Delivery failed:', msg.error);
1307
+ * }
1308
+ * }
1309
+ * ```
1310
+ */
792
1311
  async getOutbound(id: string): Promise<EmailOutbound | null> {
793
1312
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/outbound/${encodeURIComponent(id)}`);
794
1313
  const signal = AbortSignal.timeout(30_000);
@@ -811,6 +1330,19 @@ export class EmailStorageService implements EmailService {
811
1330
  throw await toServiceException('GET', url, res.response);
812
1331
  }
813
1332
 
1333
+ /**
1334
+ * Delete an outbound email by its ID.
1335
+ *
1336
+ * @remarks This operation is idempotent — deleting a non-existent email does not throw.
1337
+ *
1338
+ * @param id - The outbound email ID (prefixed with `eout_`)
1339
+ * @throws ServiceException on API errors (other than 404)
1340
+ *
1341
+ * @example
1342
+ * ```typescript
1343
+ * await email.deleteOutbound('eout_abc123');
1344
+ * ```
1345
+ */
814
1346
  async deleteOutbound(id: string): Promise<void> {
815
1347
  const url = buildUrl(this.#baseUrl, `/email/2025-03-17/outbound/${encodeURIComponent(id)}`);
816
1348
  const signal = AbortSignal.timeout(30_000);
@@ -830,6 +1362,25 @@ export class EmailStorageService implements EmailService {
830
1362
  throw await toServiceException('DELETE', url, res.response);
831
1363
  }
832
1364
 
1365
+ /**
1366
+ * Get email activity time-series data showing inbound and outbound counts per day.
1367
+ *
1368
+ * @param params - Optional query parameters. `days` controls the lookback window
1369
+ * (minimum 7, maximum 365, server default 7).
1370
+ * @returns An {@link EmailActivityResult} with daily data points ordered chronologically
1371
+ * and the total number of days returned
1372
+ * @throws ServiceException on API errors
1373
+ *
1374
+ * @example
1375
+ * ```typescript
1376
+ * // Get last 30 days of activity
1377
+ * const result = await email.getActivity({ days: 30 });
1378
+ * console.log(`Activity over ${result.days} days:`);
1379
+ * for (const point of result.activity) {
1380
+ * console.log(` ${point.date}: ${point.inbound} in, ${point.outbound} out`);
1381
+ * }
1382
+ * ```
1383
+ */
833
1384
  async getActivity(params?: EmailActivityParams): Promise<EmailActivityResult> {
834
1385
  const queryParams = new URLSearchParams();
835
1386
  if (params?.days !== undefined) {