@masters-union/outbound-sdk 0.4.4 → 0.4.5

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.mts CHANGED
@@ -335,10 +335,17 @@ interface TemplateStatsResponse {
335
335
  leastUsed: Template[];
336
336
  recentlyUsed: Template[];
337
337
  }
338
+ type SuppressionReason = 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
338
339
  interface ListSuppressionsParams {
339
340
  page?: number;
340
341
  limit?: number;
341
- reason?: 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
342
+ reason?: SuppressionReason;
343
+ /**
344
+ * Partial, case-insensitive match on the address. A comma-separated value is
345
+ * treated as several terms OR'd together (`'acme.com,gmail.com'`). For an exact
346
+ * lookup of known addresses use `search()` instead.
347
+ */
348
+ search?: string;
342
349
  }
343
350
  interface ListSuppressionsResponse {
344
351
  suppressions: Suppression[];
@@ -357,11 +364,90 @@ interface Suppression {
357
364
  }
358
365
  interface AddSuppressionParams {
359
366
  email: string;
360
- reason?: 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
367
+ reason?: SuppressionReason;
361
368
  }
362
369
  interface SuppressionResponse {
363
370
  suppression: Suppression;
364
371
  }
372
+ /** An address that failed server-side RFC validation and was never acted on. */
373
+ interface RejectedEmail {
374
+ /** The address exactly as you supplied it. */
375
+ email: string;
376
+ /** `invalid_format` — failed the RFC regex. `too_long` — over 254 characters. */
377
+ reason: 'invalid_format' | 'too_long';
378
+ }
379
+ /**
380
+ * A list of addresses. Either a comma-separated string or an array — the SDK
381
+ * normalizes both to the wire format.
382
+ */
383
+ type EmailList = string | string[];
384
+ interface SearchSuppressionsParams {
385
+ /** Addresses to look up, max 100 per request. */
386
+ emails: EmailList;
387
+ }
388
+ interface SearchSuppressionsResponse {
389
+ /** The suppression records that matched, newest first. */
390
+ suppressions: Suppression[];
391
+ /** Valid addresses that were looked up (lowercased, deduped). */
392
+ accepted: string[];
393
+ /** Addresses that failed validation and were never looked up. */
394
+ rejected: RejectedEmail[];
395
+ /** Accepted addresses that are not on the suppression list. */
396
+ notFound: string[];
397
+ summary: {
398
+ requested: number;
399
+ accepted: number;
400
+ rejected: number;
401
+ found: number;
402
+ notFound: number;
403
+ };
404
+ }
405
+ interface BulkAddSuppressionsParams {
406
+ /** Addresses to suppress, max 1000 per request. */
407
+ emails: EmailList;
408
+ /** Applied to newly added addresses only (default: `manual`). */
409
+ reason?: SuppressionReason;
410
+ }
411
+ interface BulkAddSuppressionsResponse {
412
+ /** The suppression records for every accepted address, new and pre-existing. */
413
+ suppressions: Suppression[];
414
+ /** Valid addresses that were processed (lowercased, deduped). */
415
+ accepted: string[];
416
+ /** Addresses that failed validation and were never suppressed. */
417
+ rejected: RejectedEmail[];
418
+ /** Addresses newly added by this call. */
419
+ added: string[];
420
+ /** Accepted addresses that were already on the list; their record is unchanged. */
421
+ alreadySuppressed: string[];
422
+ summary: {
423
+ requested: number;
424
+ accepted: number;
425
+ rejected: number;
426
+ added: number;
427
+ alreadySuppressed: number;
428
+ };
429
+ }
430
+ interface BulkRemoveSuppressionsParams {
431
+ /** Addresses to unsuppress, max 1000 per request. */
432
+ emails: EmailList;
433
+ }
434
+ interface BulkRemoveSuppressionsResponse {
435
+ /** Valid addresses that were processed (lowercased, deduped). */
436
+ accepted: string[];
437
+ /** Addresses that failed validation and were never removed. */
438
+ rejected: RejectedEmail[];
439
+ /** Addresses actually removed from the list by this call. */
440
+ removed: string[];
441
+ /** Accepted addresses that were not on the list to begin with. */
442
+ notFound: string[];
443
+ summary: {
444
+ requested: number;
445
+ accepted: number;
446
+ rejected: number;
447
+ removed: number;
448
+ notFound: number;
449
+ };
450
+ }
365
451
  type WebhookEvent = 'send' | 'delivery' | 'bounce' | 'complaint' | 'open' | 'click' | 'reject' | 'rendering_failure' | 'dropped' | 'unsubscribe' | 'resubscribe';
366
452
  /** @deprecated v1 is discontinued — all webhooks now use {@link IncomingWebhookEventV2}. */
367
453
  interface IncomingWebhookEvent {
@@ -773,7 +859,30 @@ declare class SuppressionsResource {
773
859
  constructor(http: HttpClient);
774
860
  list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse>;
775
861
  listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression>;
862
+ /**
863
+ * Look up specific addresses on the suppression list — exact, case-insensitive,
864
+ * up to 100 per request. Every address is validated server-side before it is
865
+ * queried, so a malformed entry comes back in `rejected` instead of failing the
866
+ * whole call.
867
+ *
868
+ * For substring matching (e.g. every address on a domain), use
869
+ * `list({ search: 'acme.com' })` instead.
870
+ */
871
+ search(params: SearchSuppressionsParams, overrides?: RequestOverrides): Promise<SearchSuppressionsResponse>;
776
872
  add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse>;
873
+ /**
874
+ * Suppress up to 1000 addresses in one request. Valid addresses are added and
875
+ * reported in `added`; ones already on the list come back in `alreadySuppressed`
876
+ * with their existing record untouched; ones that fail validation come back in
877
+ * `rejected` and are never written.
878
+ */
879
+ bulkAdd(params: BulkAddSuppressionsParams, overrides?: RequestOverrides): Promise<BulkAddSuppressionsResponse>;
880
+ /**
881
+ * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address
882
+ * that is not on the list is not an error: it is reported in `notFound`, so one
883
+ * bad entry cannot fail the batch.
884
+ */
885
+ bulkRemove(params: BulkRemoveSuppressionsParams, overrides?: RequestOverrides): Promise<BulkRemoveSuppressionsResponse>;
777
886
  remove(email: string, overrides?: RequestOverrides): Promise<{
778
887
  message: string;
779
888
  }>;
@@ -884,4 +993,4 @@ declare class NetworkError extends OutboundError {
884
993
  constructor(message?: string);
885
994
  }
886
995
 
887
- export { type AddSuppressionParams, type AnyIncomingWebhookPayload, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type BulkResponseRecipient, type CampaignAnalytics, type CampaignAnalyticsParams, type CampaignBounceReason, type CampaignDomainStat, type CampaignRates, type CampaignStatus, type CampaignSummary, type CampaignTimeseriesPoint, type CampaignTotals, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type DedicatedIpWarmup, type EmailJob, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type GlobalQuotaResponse, type IncomingWebhookEvent, type IncomingWebhookEventV2, type IncomingWebhookPayload, type IncomingWebhookPayloadV2, type IpPoolStatus, type JobStatusResponse, type ListCampaignsParams, type ListCampaignsResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, type MessageContentResponse, type MessageStatusRecipient, type MessageStatusResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent, type WebhookEventDetails, type WebhookEventStatus, type WebhookRetryStrategy };
996
+ export { type AddSuppressionParams, type AnyIncomingWebhookPayload, type Attachment, AuthenticationError, BadRequestError, type BulkAddSuppressionsParams, type BulkAddSuppressionsResponse, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type BulkRemoveSuppressionsParams, type BulkRemoveSuppressionsResponse, type BulkResponseRecipient, type CampaignAnalytics, type CampaignAnalyticsParams, type CampaignBounceReason, type CampaignDomainStat, type CampaignRates, type CampaignStatus, type CampaignSummary, type CampaignTimeseriesPoint, type CampaignTotals, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type DedicatedIpWarmup, type EmailJob, type EmailList, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type GlobalQuotaResponse, type IncomingWebhookEvent, type IncomingWebhookEventV2, type IncomingWebhookPayload, type IncomingWebhookPayloadV2, type IpPoolStatus, type JobStatusResponse, type ListCampaignsParams, type ListCampaignsResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, type MessageContentResponse, type MessageStatusRecipient, type MessageStatusResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RejectedEmail, type RequestOverrides, type ResolvedConfig, type SearchSuppressionsParams, type SearchSuppressionsResponse, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionReason, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent, type WebhookEventDetails, type WebhookEventStatus, type WebhookRetryStrategy };
package/dist/index.d.ts CHANGED
@@ -335,10 +335,17 @@ interface TemplateStatsResponse {
335
335
  leastUsed: Template[];
336
336
  recentlyUsed: Template[];
337
337
  }
338
+ type SuppressionReason = 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
338
339
  interface ListSuppressionsParams {
339
340
  page?: number;
340
341
  limit?: number;
341
- reason?: 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
342
+ reason?: SuppressionReason;
343
+ /**
344
+ * Partial, case-insensitive match on the address. A comma-separated value is
345
+ * treated as several terms OR'd together (`'acme.com,gmail.com'`). For an exact
346
+ * lookup of known addresses use `search()` instead.
347
+ */
348
+ search?: string;
342
349
  }
343
350
  interface ListSuppressionsResponse {
344
351
  suppressions: Suppression[];
@@ -357,11 +364,90 @@ interface Suppression {
357
364
  }
358
365
  interface AddSuppressionParams {
359
366
  email: string;
360
- reason?: 'bounce' | 'complaint' | 'manual' | 'unsubscribe';
367
+ reason?: SuppressionReason;
361
368
  }
362
369
  interface SuppressionResponse {
363
370
  suppression: Suppression;
364
371
  }
372
+ /** An address that failed server-side RFC validation and was never acted on. */
373
+ interface RejectedEmail {
374
+ /** The address exactly as you supplied it. */
375
+ email: string;
376
+ /** `invalid_format` — failed the RFC regex. `too_long` — over 254 characters. */
377
+ reason: 'invalid_format' | 'too_long';
378
+ }
379
+ /**
380
+ * A list of addresses. Either a comma-separated string or an array — the SDK
381
+ * normalizes both to the wire format.
382
+ */
383
+ type EmailList = string | string[];
384
+ interface SearchSuppressionsParams {
385
+ /** Addresses to look up, max 100 per request. */
386
+ emails: EmailList;
387
+ }
388
+ interface SearchSuppressionsResponse {
389
+ /** The suppression records that matched, newest first. */
390
+ suppressions: Suppression[];
391
+ /** Valid addresses that were looked up (lowercased, deduped). */
392
+ accepted: string[];
393
+ /** Addresses that failed validation and were never looked up. */
394
+ rejected: RejectedEmail[];
395
+ /** Accepted addresses that are not on the suppression list. */
396
+ notFound: string[];
397
+ summary: {
398
+ requested: number;
399
+ accepted: number;
400
+ rejected: number;
401
+ found: number;
402
+ notFound: number;
403
+ };
404
+ }
405
+ interface BulkAddSuppressionsParams {
406
+ /** Addresses to suppress, max 1000 per request. */
407
+ emails: EmailList;
408
+ /** Applied to newly added addresses only (default: `manual`). */
409
+ reason?: SuppressionReason;
410
+ }
411
+ interface BulkAddSuppressionsResponse {
412
+ /** The suppression records for every accepted address, new and pre-existing. */
413
+ suppressions: Suppression[];
414
+ /** Valid addresses that were processed (lowercased, deduped). */
415
+ accepted: string[];
416
+ /** Addresses that failed validation and were never suppressed. */
417
+ rejected: RejectedEmail[];
418
+ /** Addresses newly added by this call. */
419
+ added: string[];
420
+ /** Accepted addresses that were already on the list; their record is unchanged. */
421
+ alreadySuppressed: string[];
422
+ summary: {
423
+ requested: number;
424
+ accepted: number;
425
+ rejected: number;
426
+ added: number;
427
+ alreadySuppressed: number;
428
+ };
429
+ }
430
+ interface BulkRemoveSuppressionsParams {
431
+ /** Addresses to unsuppress, max 1000 per request. */
432
+ emails: EmailList;
433
+ }
434
+ interface BulkRemoveSuppressionsResponse {
435
+ /** Valid addresses that were processed (lowercased, deduped). */
436
+ accepted: string[];
437
+ /** Addresses that failed validation and were never removed. */
438
+ rejected: RejectedEmail[];
439
+ /** Addresses actually removed from the list by this call. */
440
+ removed: string[];
441
+ /** Accepted addresses that were not on the list to begin with. */
442
+ notFound: string[];
443
+ summary: {
444
+ requested: number;
445
+ accepted: number;
446
+ rejected: number;
447
+ removed: number;
448
+ notFound: number;
449
+ };
450
+ }
365
451
  type WebhookEvent = 'send' | 'delivery' | 'bounce' | 'complaint' | 'open' | 'click' | 'reject' | 'rendering_failure' | 'dropped' | 'unsubscribe' | 'resubscribe';
366
452
  /** @deprecated v1 is discontinued — all webhooks now use {@link IncomingWebhookEventV2}. */
367
453
  interface IncomingWebhookEvent {
@@ -773,7 +859,30 @@ declare class SuppressionsResource {
773
859
  constructor(http: HttpClient);
774
860
  list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse>;
775
861
  listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression>;
862
+ /**
863
+ * Look up specific addresses on the suppression list — exact, case-insensitive,
864
+ * up to 100 per request. Every address is validated server-side before it is
865
+ * queried, so a malformed entry comes back in `rejected` instead of failing the
866
+ * whole call.
867
+ *
868
+ * For substring matching (e.g. every address on a domain), use
869
+ * `list({ search: 'acme.com' })` instead.
870
+ */
871
+ search(params: SearchSuppressionsParams, overrides?: RequestOverrides): Promise<SearchSuppressionsResponse>;
776
872
  add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse>;
873
+ /**
874
+ * Suppress up to 1000 addresses in one request. Valid addresses are added and
875
+ * reported in `added`; ones already on the list come back in `alreadySuppressed`
876
+ * with their existing record untouched; ones that fail validation come back in
877
+ * `rejected` and are never written.
878
+ */
879
+ bulkAdd(params: BulkAddSuppressionsParams, overrides?: RequestOverrides): Promise<BulkAddSuppressionsResponse>;
880
+ /**
881
+ * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address
882
+ * that is not on the list is not an error: it is reported in `notFound`, so one
883
+ * bad entry cannot fail the batch.
884
+ */
885
+ bulkRemove(params: BulkRemoveSuppressionsParams, overrides?: RequestOverrides): Promise<BulkRemoveSuppressionsResponse>;
777
886
  remove(email: string, overrides?: RequestOverrides): Promise<{
778
887
  message: string;
779
888
  }>;
@@ -884,4 +993,4 @@ declare class NetworkError extends OutboundError {
884
993
  constructor(message?: string);
885
994
  }
886
995
 
887
- export { type AddSuppressionParams, type AnyIncomingWebhookPayload, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type BulkResponseRecipient, type CampaignAnalytics, type CampaignAnalyticsParams, type CampaignBounceReason, type CampaignDomainStat, type CampaignRates, type CampaignStatus, type CampaignSummary, type CampaignTimeseriesPoint, type CampaignTotals, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type DedicatedIpWarmup, type EmailJob, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type GlobalQuotaResponse, type IncomingWebhookEvent, type IncomingWebhookEventV2, type IncomingWebhookPayload, type IncomingWebhookPayloadV2, type IpPoolStatus, type JobStatusResponse, type ListCampaignsParams, type ListCampaignsResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, type MessageContentResponse, type MessageStatusRecipient, type MessageStatusResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent, type WebhookEventDetails, type WebhookEventStatus, type WebhookRetryStrategy };
996
+ export { type AddSuppressionParams, type AnyIncomingWebhookPayload, type Attachment, AuthenticationError, BadRequestError, type BulkAddSuppressionsParams, type BulkAddSuppressionsResponse, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type BulkRemoveSuppressionsParams, type BulkRemoveSuppressionsResponse, type BulkResponseRecipient, type CampaignAnalytics, type CampaignAnalyticsParams, type CampaignBounceReason, type CampaignDomainStat, type CampaignRates, type CampaignStatus, type CampaignSummary, type CampaignTimeseriesPoint, type CampaignTotals, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type DedicatedIpWarmup, type EmailJob, type EmailList, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type GlobalQuotaResponse, type IncomingWebhookEvent, type IncomingWebhookEventV2, type IncomingWebhookPayload, type IncomingWebhookPayloadV2, type IpPoolStatus, type JobStatusResponse, type ListCampaignsParams, type ListCampaignsResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, type MessageContentResponse, type MessageStatusRecipient, type MessageStatusResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RejectedEmail, type RequestOverrides, type ResolvedConfig, type SearchSuppressionsParams, type SearchSuppressionsResponse, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionReason, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent, type WebhookEventDetails, type WebhookEventStatus, type WebhookRetryStrategy };
package/dist/index.js CHANGED
@@ -342,6 +342,9 @@ var TemplatesResource = class {
342
342
  };
343
343
 
344
344
  // src/resources/suppressions.ts
345
+ function toEmailList(emails) {
346
+ return Array.isArray(emails) ? emails.join(",") : emails;
347
+ }
345
348
  var SuppressionsResource = class {
346
349
  constructor(http) {
347
350
  this.http = http;
@@ -361,9 +364,50 @@ var SuppressionsResource = class {
361
364
  page++;
362
365
  }
363
366
  }
367
+ /**
368
+ * Look up specific addresses on the suppression list — exact, case-insensitive,
369
+ * up to 100 per request. Every address is validated server-side before it is
370
+ * queried, so a malformed entry comes back in `rejected` instead of failing the
371
+ * whole call.
372
+ *
373
+ * For substring matching (e.g. every address on a domain), use
374
+ * `list({ search: 'acme.com' })` instead.
375
+ */
376
+ async search(params, overrides) {
377
+ return this.http.get(
378
+ "/v1/tenants/suppressions/search",
379
+ { emails: toEmailList(params.emails) },
380
+ overrides?.apiKey
381
+ );
382
+ }
364
383
  async add(params, overrides) {
365
384
  return this.http.post("/v1/tenants/suppressions", params, overrides?.apiKey);
366
385
  }
386
+ /**
387
+ * Suppress up to 1000 addresses in one request. Valid addresses are added and
388
+ * reported in `added`; ones already on the list come back in `alreadySuppressed`
389
+ * with their existing record untouched; ones that fail validation come back in
390
+ * `rejected` and are never written.
391
+ */
392
+ async bulkAdd(params, overrides) {
393
+ return this.http.post(
394
+ "/v1/tenants/suppressions/bulk/add",
395
+ { emails: toEmailList(params.emails), reason: params.reason },
396
+ overrides?.apiKey
397
+ );
398
+ }
399
+ /**
400
+ * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address
401
+ * that is not on the list is not an error: it is reported in `notFound`, so one
402
+ * bad entry cannot fail the batch.
403
+ */
404
+ async bulkRemove(params, overrides) {
405
+ return this.http.post(
406
+ "/v1/tenants/suppressions/bulk/remove",
407
+ { emails: toEmailList(params.emails) },
408
+ overrides?.apiKey
409
+ );
410
+ }
367
411
  async remove(email, overrides) {
368
412
  return this.http.delete(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);
369
413
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/resources/quota.ts","../src/resources/campaigns.ts","../src/client.ts"],"sourcesContent":["export { Outbound } from './client';\n\n// Error classes\nexport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\n// All types\nexport type {\n OutboundConfig,\n ResolvedConfig,\n RequestOverrides,\n SendEmailParams,\n Attachment,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailRecipient,\n BulkEmailResponse,\n BulkResponseRecipient,\n EmailJob,\n JobStatusResponse,\n MessageStatusResponse,\n MessageStatusRecipient,\n MessageContentResponse,\n EmailRecipientStatus,\n EmailStatus,\n CancelEmailParams,\n CancelEmailResponse,\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n TemplateBulkSendParams,\n TemplateBulkRecipient,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n ListSuppressionsParams,\n ListSuppressionsResponse,\n Suppression,\n AddSuppressionParams,\n SuppressionResponse,\n WebhookEvent,\n IncomingWebhookEvent,\n IncomingWebhookPayload,\n WebhookEventStatus,\n WebhookEventDetails,\n IncomingWebhookEventV2,\n IncomingWebhookPayloadV2,\n AnyIncomingWebhookPayload,\n WebhookRetryStrategy,\n CreateWebhookParams,\n UpdateWebhookParams,\n Webhook,\n CreateWebhookResponse,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n DashboardResponse,\n DedicatedIpWarmup,\n IpPoolStatus,\n QuotaResponse,\n GlobalQuotaResponse,\n CampaignStatus,\n ListCampaignsParams,\n CampaignSummary,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignTotals,\n CampaignRates,\n CampaignTimeseriesPoint,\n CampaignDomainStat,\n CampaignBounceReason,\n CampaignAnalytics,\n} from './types';\n","export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n MessageStatusResponse,\n MessageContentResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Look up the current status of a single message by its `messageId` — the ID\n * returned from `send()`/`bulk()` and included in webhook payloads. Useful for\n * reconciling a message whose webhook was missed.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message\n * must belong to the tenant the API key is scoped to; otherwise a `NotFoundError`\n * is thrown. The status reflects the latest event recorded by the SES event\n * pipeline.\n */\n async getMessageStatus(messageId: string, overrides?: RequestOverrides): Promise<MessageStatusResponse> {\n return this.http.get<MessageStatusResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/status`, undefined, overrides?.apiKey);\n }\n\n /**\n * Fetch the content that was sent for a single message by its `messageId` — the\n * subject, envelope and `htmlBody`/`textBody`, exactly as sent (the HTML includes\n * the appended unsubscribe footer). This is the same content the dashboard renders\n * in its \"Preview\", so you can reconstruct or re-display a sent email on your side.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message must\n * belong to the tenant the API key is scoped to; otherwise a `NotFoundError` is\n * thrown. Attachments are not inlined.\n */\n async getMessageContent(messageId: string, overrides?: RequestOverrides): Promise<MessageContentResponse> {\n return this.http.get<MessageContentResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/content`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n GlobalQuotaResponse,\n CheckQuotaResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * Account-global quota: the shared SES 24h cap across ALL tenants/sources.\n *\n * Sends are NOT gated — `email.send`/`email.bulk` always submit. This is an\n * OPT-IN routing aid: call `check(count)` before a batch to ask whether the\n * shared pool has room and atomically hold it against concurrent callers. An\n * `ok:false` is a normal answer (route that batch to a fallback vendor).\n *\n * `global()` is a cheap point-in-time read; `check()` is the atomic, concurrency-\n * safe primitive that most callers want.\n */\nexport class QuotaResource {\n constructor(private http: HttpClient) {}\n\n /** Read the global quota snapshot (point-in-time). For concurrency safety use `check()`. */\n async global(overrides?: RequestOverrides): Promise<GlobalQuotaResponse> {\n return this.http.get<GlobalQuotaResponse>('/v1/quota/global', undefined, overrides?.apiKey);\n }\n\n /**\n * Opt-in quota check. Atomically holds `count` against the shared pool and\n * returns whether it fit — so two concurrent `check(300)` calls against 500 of\n * headroom can't both succeed. `ok:false` is a normal response (route to a\n * fallback vendor), not an error. The hold auto-expires after `ttlSeconds`;\n * there is no reservationId and nothing to release.\n */\n async check(count: number, overrides?: RequestOverrides): Promise<CheckQuotaResponse> {\n return this.http.post<CheckQuotaResponse>('/v1/quota/check', { count }, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListCampaignsParams,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignAnalytics,\n RequestOverrides,\n} from '../types';\n\nexport class CampaignsResource {\n constructor(private http: HttpClient) {}\n\n /** List the tenant's campaigns with headline send stats. */\n async list(params?: ListCampaignsParams, overrides?: RequestOverrides): Promise<ListCampaignsResponse> {\n return this.http.get<ListCampaignsResponse>(\n '/v1/tenants/campaigns',\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n\n /** Full analytics report for one campaign (totals, rates, timeseries, breakdowns). */\n async analytics(\n campaignId: string,\n params?: CampaignAnalyticsParams,\n overrides?: RequestOverrides,\n ): Promise<CampaignAnalytics> {\n return this.http.get<CampaignAnalytics>(\n `/v1/tenants/campaigns/${encodeURIComponent(campaignId)}/analytics`,\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport { QuotaResource } from './resources/quota';\nimport { CampaignsResource } from './resources/campaigns';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.unionstack.in';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n readonly quota: QuotaResource;\n readonly campaigns: CampaignsResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n this.quota = new QuotaResource(http);\n this.campaigns = new CampaignsResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/resources/quota.ts","../src/resources/campaigns.ts","../src/client.ts"],"sourcesContent":["export { Outbound } from './client';\n\n// Error classes\nexport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\n// All types\nexport type {\n OutboundConfig,\n ResolvedConfig,\n RequestOverrides,\n SendEmailParams,\n Attachment,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailRecipient,\n BulkEmailResponse,\n BulkResponseRecipient,\n EmailJob,\n JobStatusResponse,\n MessageStatusResponse,\n MessageStatusRecipient,\n MessageContentResponse,\n EmailRecipientStatus,\n EmailStatus,\n CancelEmailParams,\n CancelEmailResponse,\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n TemplateBulkSendParams,\n TemplateBulkRecipient,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n ListSuppressionsParams,\n ListSuppressionsResponse,\n Suppression,\n SuppressionReason,\n AddSuppressionParams,\n SuppressionResponse,\n EmailList,\n RejectedEmail,\n SearchSuppressionsParams,\n SearchSuppressionsResponse,\n BulkAddSuppressionsParams,\n BulkAddSuppressionsResponse,\n BulkRemoveSuppressionsParams,\n BulkRemoveSuppressionsResponse,\n WebhookEvent,\n IncomingWebhookEvent,\n IncomingWebhookPayload,\n WebhookEventStatus,\n WebhookEventDetails,\n IncomingWebhookEventV2,\n IncomingWebhookPayloadV2,\n AnyIncomingWebhookPayload,\n WebhookRetryStrategy,\n CreateWebhookParams,\n UpdateWebhookParams,\n Webhook,\n CreateWebhookResponse,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n DashboardResponse,\n DedicatedIpWarmup,\n IpPoolStatus,\n QuotaResponse,\n GlobalQuotaResponse,\n CampaignStatus,\n ListCampaignsParams,\n CampaignSummary,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignTotals,\n CampaignRates,\n CampaignTimeseriesPoint,\n CampaignDomainStat,\n CampaignBounceReason,\n CampaignAnalytics,\n} from './types';\n","export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n MessageStatusResponse,\n MessageContentResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Look up the current status of a single message by its `messageId` — the ID\n * returned from `send()`/`bulk()` and included in webhook payloads. Useful for\n * reconciling a message whose webhook was missed.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message\n * must belong to the tenant the API key is scoped to; otherwise a `NotFoundError`\n * is thrown. The status reflects the latest event recorded by the SES event\n * pipeline.\n */\n async getMessageStatus(messageId: string, overrides?: RequestOverrides): Promise<MessageStatusResponse> {\n return this.http.get<MessageStatusResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/status`, undefined, overrides?.apiKey);\n }\n\n /**\n * Fetch the content that was sent for a single message by its `messageId` — the\n * subject, envelope and `htmlBody`/`textBody`, exactly as sent (the HTML includes\n * the appended unsubscribe footer). This is the same content the dashboard renders\n * in its \"Preview\", so you can reconstruct or re-display a sent email on your side.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message must\n * belong to the tenant the API key is scoped to; otherwise a `NotFoundError` is\n * thrown. Attachments are not inlined.\n */\n async getMessageContent(messageId: string, overrides?: RequestOverrides): Promise<MessageContentResponse> {\n return this.http.get<MessageContentResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/content`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n EmailList,\n SearchSuppressionsParams,\n SearchSuppressionsResponse,\n BulkAddSuppressionsParams,\n BulkAddSuppressionsResponse,\n BulkRemoveSuppressionsParams,\n BulkRemoveSuppressionsResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * The API takes a comma-separated list on the wire. Arrays are the ergonomic\n * form for callers, so normalize here rather than making every caller join.\n */\nfunction toEmailList(emails: EmailList): string {\n return Array.isArray(emails) ? emails.join(',') : emails;\n}\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n /**\n * Look up specific addresses on the suppression list — exact, case-insensitive,\n * up to 100 per request. Every address is validated server-side before it is\n * queried, so a malformed entry comes back in `rejected` instead of failing the\n * whole call.\n *\n * For substring matching (e.g. every address on a domain), use\n * `list({ search: 'acme.com' })` instead.\n */\n async search(params: SearchSuppressionsParams, overrides?: RequestOverrides): Promise<SearchSuppressionsResponse> {\n return this.http.get<SearchSuppressionsResponse>(\n '/v1/tenants/suppressions/search',\n { emails: toEmailList(params.emails) },\n overrides?.apiKey,\n );\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n /**\n * Suppress up to 1000 addresses in one request. Valid addresses are added and\n * reported in `added`; ones already on the list come back in `alreadySuppressed`\n * with their existing record untouched; ones that fail validation come back in\n * `rejected` and are never written.\n */\n async bulkAdd(params: BulkAddSuppressionsParams, overrides?: RequestOverrides): Promise<BulkAddSuppressionsResponse> {\n return this.http.post<BulkAddSuppressionsResponse>(\n '/v1/tenants/suppressions/bulk/add',\n { emails: toEmailList(params.emails), reason: params.reason },\n overrides?.apiKey,\n );\n }\n\n /**\n * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address\n * that is not on the list is not an error: it is reported in `notFound`, so one\n * bad entry cannot fail the batch.\n */\n async bulkRemove(params: BulkRemoveSuppressionsParams, overrides?: RequestOverrides): Promise<BulkRemoveSuppressionsResponse> {\n return this.http.post<BulkRemoveSuppressionsResponse>(\n '/v1/tenants/suppressions/bulk/remove',\n { emails: toEmailList(params.emails) },\n overrides?.apiKey,\n );\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n GlobalQuotaResponse,\n CheckQuotaResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * Account-global quota: the shared SES 24h cap across ALL tenants/sources.\n *\n * Sends are NOT gated — `email.send`/`email.bulk` always submit. This is an\n * OPT-IN routing aid: call `check(count)` before a batch to ask whether the\n * shared pool has room and atomically hold it against concurrent callers. An\n * `ok:false` is a normal answer (route that batch to a fallback vendor).\n *\n * `global()` is a cheap point-in-time read; `check()` is the atomic, concurrency-\n * safe primitive that most callers want.\n */\nexport class QuotaResource {\n constructor(private http: HttpClient) {}\n\n /** Read the global quota snapshot (point-in-time). For concurrency safety use `check()`. */\n async global(overrides?: RequestOverrides): Promise<GlobalQuotaResponse> {\n return this.http.get<GlobalQuotaResponse>('/v1/quota/global', undefined, overrides?.apiKey);\n }\n\n /**\n * Opt-in quota check. Atomically holds `count` against the shared pool and\n * returns whether it fit — so two concurrent `check(300)` calls against 500 of\n * headroom can't both succeed. `ok:false` is a normal response (route to a\n * fallback vendor), not an error. The hold auto-expires after `ttlSeconds`;\n * there is no reservationId and nothing to release.\n */\n async check(count: number, overrides?: RequestOverrides): Promise<CheckQuotaResponse> {\n return this.http.post<CheckQuotaResponse>('/v1/quota/check', { count }, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListCampaignsParams,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignAnalytics,\n RequestOverrides,\n} from '../types';\n\nexport class CampaignsResource {\n constructor(private http: HttpClient) {}\n\n /** List the tenant's campaigns with headline send stats. */\n async list(params?: ListCampaignsParams, overrides?: RequestOverrides): Promise<ListCampaignsResponse> {\n return this.http.get<ListCampaignsResponse>(\n '/v1/tenants/campaigns',\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n\n /** Full analytics report for one campaign (totals, rates, timeseries, breakdowns). */\n async analytics(\n campaignId: string,\n params?: CampaignAnalyticsParams,\n overrides?: RequestOverrides,\n ): Promise<CampaignAnalytics> {\n return this.http.get<CampaignAnalytics>(\n `/v1/tenants/campaigns/${encodeURIComponent(campaignId)}/analytics`,\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport { QuotaResource } from './resources/quota';\nimport { CampaignsResource } from './resources/campaigns';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.unionstack.in';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n readonly quota: QuotaResource;\n readonly campaigns: CampaignsResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n this.quota = new QuotaResource(http);\n this.campaigns = new CampaignsResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -313,6 +313,9 @@ var TemplatesResource = class {
313
313
  };
314
314
 
315
315
  // src/resources/suppressions.ts
316
+ function toEmailList(emails) {
317
+ return Array.isArray(emails) ? emails.join(",") : emails;
318
+ }
316
319
  var SuppressionsResource = class {
317
320
  constructor(http) {
318
321
  this.http = http;
@@ -332,9 +335,50 @@ var SuppressionsResource = class {
332
335
  page++;
333
336
  }
334
337
  }
338
+ /**
339
+ * Look up specific addresses on the suppression list — exact, case-insensitive,
340
+ * up to 100 per request. Every address is validated server-side before it is
341
+ * queried, so a malformed entry comes back in `rejected` instead of failing the
342
+ * whole call.
343
+ *
344
+ * For substring matching (e.g. every address on a domain), use
345
+ * `list({ search: 'acme.com' })` instead.
346
+ */
347
+ async search(params, overrides) {
348
+ return this.http.get(
349
+ "/v1/tenants/suppressions/search",
350
+ { emails: toEmailList(params.emails) },
351
+ overrides?.apiKey
352
+ );
353
+ }
335
354
  async add(params, overrides) {
336
355
  return this.http.post("/v1/tenants/suppressions", params, overrides?.apiKey);
337
356
  }
357
+ /**
358
+ * Suppress up to 1000 addresses in one request. Valid addresses are added and
359
+ * reported in `added`; ones already on the list come back in `alreadySuppressed`
360
+ * with their existing record untouched; ones that fail validation come back in
361
+ * `rejected` and are never written.
362
+ */
363
+ async bulkAdd(params, overrides) {
364
+ return this.http.post(
365
+ "/v1/tenants/suppressions/bulk/add",
366
+ { emails: toEmailList(params.emails), reason: params.reason },
367
+ overrides?.apiKey
368
+ );
369
+ }
370
+ /**
371
+ * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address
372
+ * that is not on the list is not an error: it is reported in `notFound`, so one
373
+ * bad entry cannot fail the batch.
374
+ */
375
+ async bulkRemove(params, overrides) {
376
+ return this.http.post(
377
+ "/v1/tenants/suppressions/bulk/remove",
378
+ { emails: toEmailList(params.emails) },
379
+ overrides?.apiKey
380
+ );
381
+ }
338
382
  async remove(email, overrides) {
339
383
  return this.http.delete(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);
340
384
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/resources/quota.ts","../src/resources/campaigns.ts","../src/client.ts"],"sourcesContent":["export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n MessageStatusResponse,\n MessageContentResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Look up the current status of a single message by its `messageId` — the ID\n * returned from `send()`/`bulk()` and included in webhook payloads. Useful for\n * reconciling a message whose webhook was missed.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message\n * must belong to the tenant the API key is scoped to; otherwise a `NotFoundError`\n * is thrown. The status reflects the latest event recorded by the SES event\n * pipeline.\n */\n async getMessageStatus(messageId: string, overrides?: RequestOverrides): Promise<MessageStatusResponse> {\n return this.http.get<MessageStatusResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/status`, undefined, overrides?.apiKey);\n }\n\n /**\n * Fetch the content that was sent for a single message by its `messageId` — the\n * subject, envelope and `htmlBody`/`textBody`, exactly as sent (the HTML includes\n * the appended unsubscribe footer). This is the same content the dashboard renders\n * in its \"Preview\", so you can reconstruct or re-display a sent email on your side.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message must\n * belong to the tenant the API key is scoped to; otherwise a `NotFoundError` is\n * thrown. Attachments are not inlined.\n */\n async getMessageContent(messageId: string, overrides?: RequestOverrides): Promise<MessageContentResponse> {\n return this.http.get<MessageContentResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/content`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n GlobalQuotaResponse,\n CheckQuotaResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * Account-global quota: the shared SES 24h cap across ALL tenants/sources.\n *\n * Sends are NOT gated — `email.send`/`email.bulk` always submit. This is an\n * OPT-IN routing aid: call `check(count)` before a batch to ask whether the\n * shared pool has room and atomically hold it against concurrent callers. An\n * `ok:false` is a normal answer (route that batch to a fallback vendor).\n *\n * `global()` is a cheap point-in-time read; `check()` is the atomic, concurrency-\n * safe primitive that most callers want.\n */\nexport class QuotaResource {\n constructor(private http: HttpClient) {}\n\n /** Read the global quota snapshot (point-in-time). For concurrency safety use `check()`. */\n async global(overrides?: RequestOverrides): Promise<GlobalQuotaResponse> {\n return this.http.get<GlobalQuotaResponse>('/v1/quota/global', undefined, overrides?.apiKey);\n }\n\n /**\n * Opt-in quota check. Atomically holds `count` against the shared pool and\n * returns whether it fit — so two concurrent `check(300)` calls against 500 of\n * headroom can't both succeed. `ok:false` is a normal response (route to a\n * fallback vendor), not an error. The hold auto-expires after `ttlSeconds`;\n * there is no reservationId and nothing to release.\n */\n async check(count: number, overrides?: RequestOverrides): Promise<CheckQuotaResponse> {\n return this.http.post<CheckQuotaResponse>('/v1/quota/check', { count }, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListCampaignsParams,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignAnalytics,\n RequestOverrides,\n} from '../types';\n\nexport class CampaignsResource {\n constructor(private http: HttpClient) {}\n\n /** List the tenant's campaigns with headline send stats. */\n async list(params?: ListCampaignsParams, overrides?: RequestOverrides): Promise<ListCampaignsResponse> {\n return this.http.get<ListCampaignsResponse>(\n '/v1/tenants/campaigns',\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n\n /** Full analytics report for one campaign (totals, rates, timeseries, breakdowns). */\n async analytics(\n campaignId: string,\n params?: CampaignAnalyticsParams,\n overrides?: RequestOverrides,\n ): Promise<CampaignAnalytics> {\n return this.http.get<CampaignAnalytics>(\n `/v1/tenants/campaigns/${encodeURIComponent(campaignId)}/analytics`,\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport { QuotaResource } from './resources/quota';\nimport { CampaignsResource } from './resources/campaigns';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.unionstack.in';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n readonly quota: QuotaResource;\n readonly campaigns: CampaignsResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n this.quota = new QuotaResource(http);\n this.campaigns = new CampaignsResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/resources/quota.ts","../src/resources/campaigns.ts","../src/client.ts"],"sourcesContent":["export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n MessageStatusResponse,\n MessageContentResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Look up the current status of a single message by its `messageId` — the ID\n * returned from `send()`/`bulk()` and included in webhook payloads. Useful for\n * reconciling a message whose webhook was missed.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message\n * must belong to the tenant the API key is scoped to; otherwise a `NotFoundError`\n * is thrown. The status reflects the latest event recorded by the SES event\n * pipeline.\n */\n async getMessageStatus(messageId: string, overrides?: RequestOverrides): Promise<MessageStatusResponse> {\n return this.http.get<MessageStatusResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/status`, undefined, overrides?.apiKey);\n }\n\n /**\n * Fetch the content that was sent for a single message by its `messageId` — the\n * subject, envelope and `htmlBody`/`textBody`, exactly as sent (the HTML includes\n * the appended unsubscribe footer). This is the same content the dashboard renders\n * in its \"Preview\", so you can reconstruct or re-display a sent email on your side.\n *\n * Accepts **only** a message ID — not a job ID or SES message ID. The message must\n * belong to the tenant the API key is scoped to; otherwise a `NotFoundError` is\n * thrown. Attachments are not inlined.\n */\n async getMessageContent(messageId: string, overrides?: RequestOverrides): Promise<MessageContentResponse> {\n return this.http.get<MessageContentResponse>(`/v1/email/messages/${encodeURIComponent(messageId)}/content`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n EmailList,\n SearchSuppressionsParams,\n SearchSuppressionsResponse,\n BulkAddSuppressionsParams,\n BulkAddSuppressionsResponse,\n BulkRemoveSuppressionsParams,\n BulkRemoveSuppressionsResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * The API takes a comma-separated list on the wire. Arrays are the ergonomic\n * form for callers, so normalize here rather than making every caller join.\n */\nfunction toEmailList(emails: EmailList): string {\n return Array.isArray(emails) ? emails.join(',') : emails;\n}\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n /**\n * Look up specific addresses on the suppression list — exact, case-insensitive,\n * up to 100 per request. Every address is validated server-side before it is\n * queried, so a malformed entry comes back in `rejected` instead of failing the\n * whole call.\n *\n * For substring matching (e.g. every address on a domain), use\n * `list({ search: 'acme.com' })` instead.\n */\n async search(params: SearchSuppressionsParams, overrides?: RequestOverrides): Promise<SearchSuppressionsResponse> {\n return this.http.get<SearchSuppressionsResponse>(\n '/v1/tenants/suppressions/search',\n { emails: toEmailList(params.emails) },\n overrides?.apiKey,\n );\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n /**\n * Suppress up to 1000 addresses in one request. Valid addresses are added and\n * reported in `added`; ones already on the list come back in `alreadySuppressed`\n * with their existing record untouched; ones that fail validation come back in\n * `rejected` and are never written.\n */\n async bulkAdd(params: BulkAddSuppressionsParams, overrides?: RequestOverrides): Promise<BulkAddSuppressionsResponse> {\n return this.http.post<BulkAddSuppressionsResponse>(\n '/v1/tenants/suppressions/bulk/add',\n { emails: toEmailList(params.emails), reason: params.reason },\n overrides?.apiKey,\n );\n }\n\n /**\n * Unsuppress up to 1000 addresses in one request. Unlike `remove()`, an address\n * that is not on the list is not an error: it is reported in `notFound`, so one\n * bad entry cannot fail the batch.\n */\n async bulkRemove(params: BulkRemoveSuppressionsParams, overrides?: RequestOverrides): Promise<BulkRemoveSuppressionsResponse> {\n return this.http.post<BulkRemoveSuppressionsResponse>(\n '/v1/tenants/suppressions/bulk/remove',\n { emails: toEmailList(params.emails) },\n overrides?.apiKey,\n );\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n GlobalQuotaResponse,\n CheckQuotaResponse,\n RequestOverrides,\n} from '../types';\n\n/**\n * Account-global quota: the shared SES 24h cap across ALL tenants/sources.\n *\n * Sends are NOT gated — `email.send`/`email.bulk` always submit. This is an\n * OPT-IN routing aid: call `check(count)` before a batch to ask whether the\n * shared pool has room and atomically hold it against concurrent callers. An\n * `ok:false` is a normal answer (route that batch to a fallback vendor).\n *\n * `global()` is a cheap point-in-time read; `check()` is the atomic, concurrency-\n * safe primitive that most callers want.\n */\nexport class QuotaResource {\n constructor(private http: HttpClient) {}\n\n /** Read the global quota snapshot (point-in-time). For concurrency safety use `check()`. */\n async global(overrides?: RequestOverrides): Promise<GlobalQuotaResponse> {\n return this.http.get<GlobalQuotaResponse>('/v1/quota/global', undefined, overrides?.apiKey);\n }\n\n /**\n * Opt-in quota check. Atomically holds `count` against the shared pool and\n * returns whether it fit — so two concurrent `check(300)` calls against 500 of\n * headroom can't both succeed. `ok:false` is a normal response (route to a\n * fallback vendor), not an error. The hold auto-expires after `ttlSeconds`;\n * there is no reservationId and nothing to release.\n */\n async check(count: number, overrides?: RequestOverrides): Promise<CheckQuotaResponse> {\n return this.http.post<CheckQuotaResponse>('/v1/quota/check', { count }, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListCampaignsParams,\n ListCampaignsResponse,\n CampaignAnalyticsParams,\n CampaignAnalytics,\n RequestOverrides,\n} from '../types';\n\nexport class CampaignsResource {\n constructor(private http: HttpClient) {}\n\n /** List the tenant's campaigns with headline send stats. */\n async list(params?: ListCampaignsParams, overrides?: RequestOverrides): Promise<ListCampaignsResponse> {\n return this.http.get<ListCampaignsResponse>(\n '/v1/tenants/campaigns',\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n\n /** Full analytics report for one campaign (totals, rates, timeseries, breakdowns). */\n async analytics(\n campaignId: string,\n params?: CampaignAnalyticsParams,\n overrides?: RequestOverrides,\n ): Promise<CampaignAnalytics> {\n return this.http.get<CampaignAnalytics>(\n `/v1/tenants/campaigns/${encodeURIComponent(campaignId)}/analytics`,\n params as Record<string, unknown> | undefined,\n overrides?.apiKey,\n );\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport { QuotaResource } from './resources/quota';\nimport { CampaignsResource } from './resources/campaigns';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.unionstack.in';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n readonly quota: QuotaResource;\n readonly campaigns: CampaignsResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n this.quota = new QuotaResource(http);\n this.campaigns = new CampaignsResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masters-union/outbound-sdk",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "Official Node.js SDK for the Outbound Email SaaS platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",