@stack0/sdk 0.5.17 → 0.6.1

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.
@@ -906,6 +906,14 @@ interface Mailbox {
906
906
  createdAt: Date | string;
907
907
  updatedAt?: Date | string;
908
908
  }
909
+ /** A mailbox as `create` returns it, with its webhook signing secret. Store the secret: only `create` and `rotateSecret` return it. */
910
+ interface MailboxWithSecret extends Mailbox {
911
+ webhookSecret: string;
912
+ }
913
+ interface RotateMailboxSecretResponse {
914
+ id: string;
915
+ webhookSecret: string;
916
+ }
909
917
  interface CreateMailboxRequest {
910
918
  projectSlug?: string;
911
919
  environment?: Environment;
@@ -945,12 +953,18 @@ interface InboundAttachment {
945
953
  filename: string;
946
954
  contentType: string;
947
955
  size: number;
948
- url?: string;
956
+ /** Presigned GET URL, valid until `expiresAt` (one hour after the webhook attempt or API read). Null when the attachment could not be signed. */
957
+ url: string | null;
958
+ expiresAt: string | null;
949
959
  }
960
+ /** Headers of an inbound email: lowercased name to value, or to every value of a repeated header (Received). */
961
+ type InboundHeaders = Record<string, string | string[]>;
950
962
  interface InboundMessage {
951
963
  id: string;
952
964
  mailboxId: string;
953
965
  mailbox: string;
966
+ /** The part after "+" when the email came to `local+tag@domain` and routed to the mailbox `local@domain`. */
967
+ tag: string | null;
954
968
  messageId?: string;
955
969
  inReplyTo?: string;
956
970
  references?: string;
@@ -962,9 +976,12 @@ interface InboundMessage {
962
976
  subject?: string;
963
977
  html?: string;
964
978
  text?: string;
979
+ headers: InboundHeaders;
965
980
  attachments?: InboundAttachment[];
966
981
  webhookDelivered: boolean;
967
982
  webhookAttempts: number;
983
+ /** Why the last delivery failed, or `DAILY_LIMIT_REACHED: ...` when the message arrived over `maxInboundPerDay` and was not delivered. */
984
+ webhookLastError: string | null;
968
985
  createdAt: Date | string;
969
986
  }
970
987
  interface ListInboundMessagesRequest {
@@ -977,10 +994,18 @@ interface ListInboundMessagesResponse {
977
994
  hasMore: boolean;
978
995
  nextCursor?: string;
979
996
  }
997
+ /**
998
+ * The body of an `email.inbound` webhook. Verify it with `verifyInboundWebhook`
999
+ * before trusting it. `id` is the same on every retry of one message
1000
+ * (and equals the `webhook-id` header), so deduplicate on it.
1001
+ */
980
1002
  interface InboundWebhookPayload {
1003
+ id: string;
981
1004
  event: "email.inbound";
982
1005
  mailbox: string;
983
1006
  mailboxId: string;
1007
+ /** The part after "+" when the email came to `local+tag@domain` and routed to the mailbox `local@domain`. */
1008
+ tag: string | null;
984
1009
  from: InboundEmailAddress;
985
1010
  to: string;
986
1011
  cc?: string[];
@@ -992,6 +1017,7 @@ interface InboundWebhookPayload {
992
1017
  messageId?: string;
993
1018
  inReplyTo?: string;
994
1019
  references?: string[];
1020
+ headers: InboundHeaders;
995
1021
  attachments?: InboundAttachment[];
996
1022
  metadata?: Record<string, unknown>;
997
1023
  receivedAt: string;
@@ -1212,9 +1238,15 @@ declare class Mailboxes {
1212
1238
  private http;
1213
1239
  constructor(http: HttpClient);
1214
1240
  /**
1215
- * Create a new mailbox
1241
+ * Create a new mailbox. The result carries `webhookSecret`, which signs every
1242
+ * inbound delivery; store it now (only `create` and `rotateSecret` return it).
1243
+ */
1244
+ create(request: CreateMailboxRequest): Promise<MailboxWithSecret>;
1245
+ /**
1246
+ * Replace a mailbox's webhook secret and return the new one. Deliveries are
1247
+ * signed with the new secret from then on.
1216
1248
  */
1217
- create(request: CreateMailboxRequest): Promise<Mailbox>;
1249
+ rotateSecret(id: string): Promise<RotateMailboxSecretResponse>;
1218
1250
  /**
1219
1251
  * Get a mailbox by ID
1220
1252
  */
@@ -1519,4 +1551,44 @@ declare class Mail {
1519
1551
  listSenders(request?: ListSendersRequest): Promise<ListSendersResponse>;
1520
1552
  }
1521
1553
 
1522
- export { type AddContactToSequenceRequest, type AddContactsToAudienceRequest, type AddContactsToAudienceResponse, type AddDomainRequest, type AddDomainResponse, type ArchiveSequenceResponse, type Attachment, type Audience, type AudienceContact, Audiences, type BatchTrackEventsRequest, type BatchTrackEventsResponse, type Campaign, type CampaignStatsResponse, type CampaignStatus, Campaigns, type CancelCampaignResponse, type CancelEmailResponse, type ConnectionType, type ContactStatus, Contacts, type CreateAudienceRequest, type CreateCampaignRequest, type CreateConnectionRequest, type CreateContactRequest, type CreateEventRequest, type CreateMailboxRequest, type CreateNodeRequest, type CreateSequenceRequest, type CreateTemplateRequest, type DeleteAudienceResponse, type DeleteCampaignResponse, type DeleteConnectionResponse, type DeleteContactResponse, type DeleteDomainResponse, type DeleteEventResponse, type DeleteNodeResponse, type DeleteSequenceResponse, type DeleteTemplateResponse, type DnsRecord, type Domain, type DomainStatus, Domains, type Email, type EmailAddress, type EmailAnalyticsResponse, type EmailRecipient, type EmailStatus, type EventAnalyticsResponse, type EventOccurrence, type EventPropertiesSchema, type EventProperty, Events, type GetDnsRecordsResponse, type GetEmailResponse, type HourlyAnalyticsDataPoint, type HourlyAnalyticsResponse, type ImportContactsRequest, type ImportContactsResponse, type InboundAttachment, type InboundEmailAddress, type InboundMessage, type InboundWebhookPayload, type ListAudienceContactsRequest, type ListAudienceContactsResponse, type ListAudiencesRequest, type ListAudiencesResponse, type ListCampaignsRequest, type ListCampaignsResponse, type ListContactsRequest, type ListContactsResponse, type ListDomainsRequest, type ListDomainsResponse, type ListEmailsRequest, type ListEmailsResponse, type ListEventOccurrencesRequest, type ListEventOccurrencesResponse, type ListEventsRequest, type ListEventsResponse, type ListInboundMessagesRequest, type ListInboundMessagesResponse, type ListMailboxesRequest, type ListMailboxesResponse, type ListSendersRequest, type ListSendersResponse, type ListSequenceEntriesRequest, type ListSequenceEntriesResponse, type ListSequencesRequest, type ListSequencesResponse, type ListTemplatesRequest, type ListTemplatesResponse, Mail, type MailContact, type MailEvent, type Mailbox, Mailboxes, type PauseCampaignResponse, type PauseSequenceResponse, type PreviewTemplateRequest, type PreviewTemplateResponse, type PublishSequenceResponse, type RemoveContactFromSequenceRequest, type RemoveContactFromSequenceResponse, type RemoveContactsFromAudienceRequest, type RemoveContactsFromAudienceResponse, type ResendEmailResponse, type ResumeSequenceResponse, type SendBatchEmailRequest, type SendBatchEmailResponse, type SendBroadcastEmailRequest, type SendBroadcastEmailResponse, type SendCampaignRequest, type SendCampaignResponse, type SendEmailRequest, type SendEmailResponse, type Sender, type Sequence, type SequenceAnalyticsResponse, type SequenceConnection, type SequenceEntry, type SequenceEntryStatus, type SequenceNode, type SequenceNodeType, type SequenceStatus, type SequenceTriggerFrequency, type SequenceTriggerType, type SequenceWithNodes, Sequences, type SetDefaultDomainResponse, type SetNodeBranchRequest, type SetNodeEmailRequest, type SetNodeExperimentRequest, type SetNodeFilterRequest, type SetNodeTimerRequest, type Template, Templates, type TimeSeriesAnalyticsRequest, type TimeSeriesAnalyticsResponse, type TimeSeriesDataPoint, type TrackEventRequest, type TrackEventResponse, type UpdateAudienceRequest, type UpdateCampaignRequest, type UpdateContactRequest, type UpdateEventRequest, type UpdateMailboxRequest, type UpdateNodePositionRequest, type UpdateNodeRequest, type UpdateSequenceRequest, type UpdateTemplateRequest, type VerifyDomainResponse };
1554
+ /**
1555
+ * Verify inbound email webhooks.
1556
+ *
1557
+ * Stack0 signs each delivery with the Standard Webhooks scheme
1558
+ * (https://www.standardwebhooks.com): `webhook-signature` is
1559
+ * `v1,<base64 HMAC-SHA256 of "<webhook-id>.<webhook-timestamp>.<raw body>">`,
1560
+ * keyed by the mailbox secret, base64-decoded. Uses Web Crypto, so it runs in
1561
+ * Node 18+, Bun, Deno, and edge runtimes.
1562
+ */
1563
+
1564
+ declare class WebhookVerificationError extends Error {
1565
+ constructor(message: string);
1566
+ }
1567
+ interface VerifyInboundWebhookOptions {
1568
+ /** The raw request body, exactly as received. Do not parse and re-serialize it first. */
1569
+ payload: string | Uint8Array | ArrayBuffer;
1570
+ /** The request headers: a `Headers` object or a plain record (any case). */
1571
+ headers: Headers | Record<string, string | string[] | undefined>;
1572
+ /** The mailbox's webhook secret, from `mailboxes.create` or `mailboxes.rotateSecret`. */
1573
+ secret: string;
1574
+ /** How far the timestamp may be from now, in seconds. Default 300. */
1575
+ toleranceSeconds?: number;
1576
+ /** The current time, for tests. */
1577
+ now?: Date;
1578
+ }
1579
+ /**
1580
+ * Check an inbound email webhook and return its payload. Throws
1581
+ * `WebhookVerificationError` when a header is missing, the timestamp is
1582
+ * outside the tolerance (a replay), or no signature matches.
1583
+ *
1584
+ * ```ts
1585
+ * const event = await verifyInboundWebhook({
1586
+ * payload: await request.text(),
1587
+ * headers: request.headers,
1588
+ * secret: process.env.STACK0_MAILBOX_SECRET!,
1589
+ * });
1590
+ * ```
1591
+ */
1592
+ declare function verifyInboundWebhook(options: VerifyInboundWebhookOptions): Promise<InboundWebhookPayload>;
1593
+
1594
+ export { type AddContactToSequenceRequest, type AddContactsToAudienceRequest, type AddContactsToAudienceResponse, type AddDomainRequest, type AddDomainResponse, type ArchiveSequenceResponse, type Attachment, type Audience, type AudienceContact, Audiences, type BatchTrackEventsRequest, type BatchTrackEventsResponse, type Campaign, type CampaignStatsResponse, type CampaignStatus, Campaigns, type CancelCampaignResponse, type CancelEmailResponse, type ConnectionType, type ContactStatus, Contacts, type CreateAudienceRequest, type CreateCampaignRequest, type CreateConnectionRequest, type CreateContactRequest, type CreateEventRequest, type CreateMailboxRequest, type CreateNodeRequest, type CreateSequenceRequest, type CreateTemplateRequest, type DeleteAudienceResponse, type DeleteCampaignResponse, type DeleteConnectionResponse, type DeleteContactResponse, type DeleteDomainResponse, type DeleteEventResponse, type DeleteNodeResponse, type DeleteSequenceResponse, type DeleteTemplateResponse, type DnsRecord, type Domain, type DomainStatus, Domains, type Email, type EmailAddress, type EmailAnalyticsResponse, type EmailRecipient, type EmailStatus, type EventAnalyticsResponse, type EventOccurrence, type EventPropertiesSchema, type EventProperty, Events, type GetDnsRecordsResponse, type GetEmailResponse, type HourlyAnalyticsDataPoint, type HourlyAnalyticsResponse, type ImportContactsRequest, type ImportContactsResponse, type InboundAttachment, type InboundEmailAddress, type InboundHeaders, type InboundMessage, type InboundWebhookPayload, type ListAudienceContactsRequest, type ListAudienceContactsResponse, type ListAudiencesRequest, type ListAudiencesResponse, type ListCampaignsRequest, type ListCampaignsResponse, type ListContactsRequest, type ListContactsResponse, type ListDomainsRequest, type ListDomainsResponse, type ListEmailsRequest, type ListEmailsResponse, type ListEventOccurrencesRequest, type ListEventOccurrencesResponse, type ListEventsRequest, type ListEventsResponse, type ListInboundMessagesRequest, type ListInboundMessagesResponse, type ListMailboxesRequest, type ListMailboxesResponse, type ListSendersRequest, type ListSendersResponse, type ListSequenceEntriesRequest, type ListSequenceEntriesResponse, type ListSequencesRequest, type ListSequencesResponse, type ListTemplatesRequest, type ListTemplatesResponse, Mail, type MailContact, type MailEvent, type Mailbox, type MailboxWithSecret, Mailboxes, type PauseCampaignResponse, type PauseSequenceResponse, type PreviewTemplateRequest, type PreviewTemplateResponse, type PublishSequenceResponse, type RemoveContactFromSequenceRequest, type RemoveContactFromSequenceResponse, type RemoveContactsFromAudienceRequest, type RemoveContactsFromAudienceResponse, type ResendEmailResponse, type ResumeSequenceResponse, type RotateMailboxSecretResponse, type SendBatchEmailRequest, type SendBatchEmailResponse, type SendBroadcastEmailRequest, type SendBroadcastEmailResponse, type SendCampaignRequest, type SendCampaignResponse, type SendEmailRequest, type SendEmailResponse, type Sender, type Sequence, type SequenceAnalyticsResponse, type SequenceConnection, type SequenceEntry, type SequenceEntryStatus, type SequenceNode, type SequenceNodeType, type SequenceStatus, type SequenceTriggerFrequency, type SequenceTriggerType, type SequenceWithNodes, Sequences, type SetDefaultDomainResponse, type SetNodeBranchRequest, type SetNodeEmailRequest, type SetNodeExperimentRequest, type SetNodeFilterRequest, type SetNodeTimerRequest, type Template, Templates, type TimeSeriesAnalyticsRequest, type TimeSeriesAnalyticsResponse, type TimeSeriesDataPoint, type TrackEventRequest, type TrackEventResponse, type UpdateAudienceRequest, type UpdateCampaignRequest, type UpdateContactRequest, type UpdateEventRequest, type UpdateMailboxRequest, type UpdateNodePositionRequest, type UpdateNodeRequest, type UpdateSequenceRequest, type UpdateTemplateRequest, type VerifyDomainResponse, type VerifyInboundWebhookOptions, WebhookVerificationError, verifyInboundWebhook };
@@ -906,6 +906,14 @@ interface Mailbox {
906
906
  createdAt: Date | string;
907
907
  updatedAt?: Date | string;
908
908
  }
909
+ /** A mailbox as `create` returns it, with its webhook signing secret. Store the secret: only `create` and `rotateSecret` return it. */
910
+ interface MailboxWithSecret extends Mailbox {
911
+ webhookSecret: string;
912
+ }
913
+ interface RotateMailboxSecretResponse {
914
+ id: string;
915
+ webhookSecret: string;
916
+ }
909
917
  interface CreateMailboxRequest {
910
918
  projectSlug?: string;
911
919
  environment?: Environment;
@@ -945,12 +953,18 @@ interface InboundAttachment {
945
953
  filename: string;
946
954
  contentType: string;
947
955
  size: number;
948
- url?: string;
956
+ /** Presigned GET URL, valid until `expiresAt` (one hour after the webhook attempt or API read). Null when the attachment could not be signed. */
957
+ url: string | null;
958
+ expiresAt: string | null;
949
959
  }
960
+ /** Headers of an inbound email: lowercased name to value, or to every value of a repeated header (Received). */
961
+ type InboundHeaders = Record<string, string | string[]>;
950
962
  interface InboundMessage {
951
963
  id: string;
952
964
  mailboxId: string;
953
965
  mailbox: string;
966
+ /** The part after "+" when the email came to `local+tag@domain` and routed to the mailbox `local@domain`. */
967
+ tag: string | null;
954
968
  messageId?: string;
955
969
  inReplyTo?: string;
956
970
  references?: string;
@@ -962,9 +976,12 @@ interface InboundMessage {
962
976
  subject?: string;
963
977
  html?: string;
964
978
  text?: string;
979
+ headers: InboundHeaders;
965
980
  attachments?: InboundAttachment[];
966
981
  webhookDelivered: boolean;
967
982
  webhookAttempts: number;
983
+ /** Why the last delivery failed, or `DAILY_LIMIT_REACHED: ...` when the message arrived over `maxInboundPerDay` and was not delivered. */
984
+ webhookLastError: string | null;
968
985
  createdAt: Date | string;
969
986
  }
970
987
  interface ListInboundMessagesRequest {
@@ -977,10 +994,18 @@ interface ListInboundMessagesResponse {
977
994
  hasMore: boolean;
978
995
  nextCursor?: string;
979
996
  }
997
+ /**
998
+ * The body of an `email.inbound` webhook. Verify it with `verifyInboundWebhook`
999
+ * before trusting it. `id` is the same on every retry of one message
1000
+ * (and equals the `webhook-id` header), so deduplicate on it.
1001
+ */
980
1002
  interface InboundWebhookPayload {
1003
+ id: string;
981
1004
  event: "email.inbound";
982
1005
  mailbox: string;
983
1006
  mailboxId: string;
1007
+ /** The part after "+" when the email came to `local+tag@domain` and routed to the mailbox `local@domain`. */
1008
+ tag: string | null;
984
1009
  from: InboundEmailAddress;
985
1010
  to: string;
986
1011
  cc?: string[];
@@ -992,6 +1017,7 @@ interface InboundWebhookPayload {
992
1017
  messageId?: string;
993
1018
  inReplyTo?: string;
994
1019
  references?: string[];
1020
+ headers: InboundHeaders;
995
1021
  attachments?: InboundAttachment[];
996
1022
  metadata?: Record<string, unknown>;
997
1023
  receivedAt: string;
@@ -1212,9 +1238,15 @@ declare class Mailboxes {
1212
1238
  private http;
1213
1239
  constructor(http: HttpClient);
1214
1240
  /**
1215
- * Create a new mailbox
1241
+ * Create a new mailbox. The result carries `webhookSecret`, which signs every
1242
+ * inbound delivery; store it now (only `create` and `rotateSecret` return it).
1243
+ */
1244
+ create(request: CreateMailboxRequest): Promise<MailboxWithSecret>;
1245
+ /**
1246
+ * Replace a mailbox's webhook secret and return the new one. Deliveries are
1247
+ * signed with the new secret from then on.
1216
1248
  */
1217
- create(request: CreateMailboxRequest): Promise<Mailbox>;
1249
+ rotateSecret(id: string): Promise<RotateMailboxSecretResponse>;
1218
1250
  /**
1219
1251
  * Get a mailbox by ID
1220
1252
  */
@@ -1519,4 +1551,44 @@ declare class Mail {
1519
1551
  listSenders(request?: ListSendersRequest): Promise<ListSendersResponse>;
1520
1552
  }
1521
1553
 
1522
- export { type AddContactToSequenceRequest, type AddContactsToAudienceRequest, type AddContactsToAudienceResponse, type AddDomainRequest, type AddDomainResponse, type ArchiveSequenceResponse, type Attachment, type Audience, type AudienceContact, Audiences, type BatchTrackEventsRequest, type BatchTrackEventsResponse, type Campaign, type CampaignStatsResponse, type CampaignStatus, Campaigns, type CancelCampaignResponse, type CancelEmailResponse, type ConnectionType, type ContactStatus, Contacts, type CreateAudienceRequest, type CreateCampaignRequest, type CreateConnectionRequest, type CreateContactRequest, type CreateEventRequest, type CreateMailboxRequest, type CreateNodeRequest, type CreateSequenceRequest, type CreateTemplateRequest, type DeleteAudienceResponse, type DeleteCampaignResponse, type DeleteConnectionResponse, type DeleteContactResponse, type DeleteDomainResponse, type DeleteEventResponse, type DeleteNodeResponse, type DeleteSequenceResponse, type DeleteTemplateResponse, type DnsRecord, type Domain, type DomainStatus, Domains, type Email, type EmailAddress, type EmailAnalyticsResponse, type EmailRecipient, type EmailStatus, type EventAnalyticsResponse, type EventOccurrence, type EventPropertiesSchema, type EventProperty, Events, type GetDnsRecordsResponse, type GetEmailResponse, type HourlyAnalyticsDataPoint, type HourlyAnalyticsResponse, type ImportContactsRequest, type ImportContactsResponse, type InboundAttachment, type InboundEmailAddress, type InboundMessage, type InboundWebhookPayload, type ListAudienceContactsRequest, type ListAudienceContactsResponse, type ListAudiencesRequest, type ListAudiencesResponse, type ListCampaignsRequest, type ListCampaignsResponse, type ListContactsRequest, type ListContactsResponse, type ListDomainsRequest, type ListDomainsResponse, type ListEmailsRequest, type ListEmailsResponse, type ListEventOccurrencesRequest, type ListEventOccurrencesResponse, type ListEventsRequest, type ListEventsResponse, type ListInboundMessagesRequest, type ListInboundMessagesResponse, type ListMailboxesRequest, type ListMailboxesResponse, type ListSendersRequest, type ListSendersResponse, type ListSequenceEntriesRequest, type ListSequenceEntriesResponse, type ListSequencesRequest, type ListSequencesResponse, type ListTemplatesRequest, type ListTemplatesResponse, Mail, type MailContact, type MailEvent, type Mailbox, Mailboxes, type PauseCampaignResponse, type PauseSequenceResponse, type PreviewTemplateRequest, type PreviewTemplateResponse, type PublishSequenceResponse, type RemoveContactFromSequenceRequest, type RemoveContactFromSequenceResponse, type RemoveContactsFromAudienceRequest, type RemoveContactsFromAudienceResponse, type ResendEmailResponse, type ResumeSequenceResponse, type SendBatchEmailRequest, type SendBatchEmailResponse, type SendBroadcastEmailRequest, type SendBroadcastEmailResponse, type SendCampaignRequest, type SendCampaignResponse, type SendEmailRequest, type SendEmailResponse, type Sender, type Sequence, type SequenceAnalyticsResponse, type SequenceConnection, type SequenceEntry, type SequenceEntryStatus, type SequenceNode, type SequenceNodeType, type SequenceStatus, type SequenceTriggerFrequency, type SequenceTriggerType, type SequenceWithNodes, Sequences, type SetDefaultDomainResponse, type SetNodeBranchRequest, type SetNodeEmailRequest, type SetNodeExperimentRequest, type SetNodeFilterRequest, type SetNodeTimerRequest, type Template, Templates, type TimeSeriesAnalyticsRequest, type TimeSeriesAnalyticsResponse, type TimeSeriesDataPoint, type TrackEventRequest, type TrackEventResponse, type UpdateAudienceRequest, type UpdateCampaignRequest, type UpdateContactRequest, type UpdateEventRequest, type UpdateMailboxRequest, type UpdateNodePositionRequest, type UpdateNodeRequest, type UpdateSequenceRequest, type UpdateTemplateRequest, type VerifyDomainResponse };
1554
+ /**
1555
+ * Verify inbound email webhooks.
1556
+ *
1557
+ * Stack0 signs each delivery with the Standard Webhooks scheme
1558
+ * (https://www.standardwebhooks.com): `webhook-signature` is
1559
+ * `v1,<base64 HMAC-SHA256 of "<webhook-id>.<webhook-timestamp>.<raw body>">`,
1560
+ * keyed by the mailbox secret, base64-decoded. Uses Web Crypto, so it runs in
1561
+ * Node 18+, Bun, Deno, and edge runtimes.
1562
+ */
1563
+
1564
+ declare class WebhookVerificationError extends Error {
1565
+ constructor(message: string);
1566
+ }
1567
+ interface VerifyInboundWebhookOptions {
1568
+ /** The raw request body, exactly as received. Do not parse and re-serialize it first. */
1569
+ payload: string | Uint8Array | ArrayBuffer;
1570
+ /** The request headers: a `Headers` object or a plain record (any case). */
1571
+ headers: Headers | Record<string, string | string[] | undefined>;
1572
+ /** The mailbox's webhook secret, from `mailboxes.create` or `mailboxes.rotateSecret`. */
1573
+ secret: string;
1574
+ /** How far the timestamp may be from now, in seconds. Default 300. */
1575
+ toleranceSeconds?: number;
1576
+ /** The current time, for tests. */
1577
+ now?: Date;
1578
+ }
1579
+ /**
1580
+ * Check an inbound email webhook and return its payload. Throws
1581
+ * `WebhookVerificationError` when a header is missing, the timestamp is
1582
+ * outside the tolerance (a replay), or no signature matches.
1583
+ *
1584
+ * ```ts
1585
+ * const event = await verifyInboundWebhook({
1586
+ * payload: await request.text(),
1587
+ * headers: request.headers,
1588
+ * secret: process.env.STACK0_MAILBOX_SECRET!,
1589
+ * });
1590
+ * ```
1591
+ */
1592
+ declare function verifyInboundWebhook(options: VerifyInboundWebhookOptions): Promise<InboundWebhookPayload>;
1593
+
1594
+ export { type AddContactToSequenceRequest, type AddContactsToAudienceRequest, type AddContactsToAudienceResponse, type AddDomainRequest, type AddDomainResponse, type ArchiveSequenceResponse, type Attachment, type Audience, type AudienceContact, Audiences, type BatchTrackEventsRequest, type BatchTrackEventsResponse, type Campaign, type CampaignStatsResponse, type CampaignStatus, Campaigns, type CancelCampaignResponse, type CancelEmailResponse, type ConnectionType, type ContactStatus, Contacts, type CreateAudienceRequest, type CreateCampaignRequest, type CreateConnectionRequest, type CreateContactRequest, type CreateEventRequest, type CreateMailboxRequest, type CreateNodeRequest, type CreateSequenceRequest, type CreateTemplateRequest, type DeleteAudienceResponse, type DeleteCampaignResponse, type DeleteConnectionResponse, type DeleteContactResponse, type DeleteDomainResponse, type DeleteEventResponse, type DeleteNodeResponse, type DeleteSequenceResponse, type DeleteTemplateResponse, type DnsRecord, type Domain, type DomainStatus, Domains, type Email, type EmailAddress, type EmailAnalyticsResponse, type EmailRecipient, type EmailStatus, type EventAnalyticsResponse, type EventOccurrence, type EventPropertiesSchema, type EventProperty, Events, type GetDnsRecordsResponse, type GetEmailResponse, type HourlyAnalyticsDataPoint, type HourlyAnalyticsResponse, type ImportContactsRequest, type ImportContactsResponse, type InboundAttachment, type InboundEmailAddress, type InboundHeaders, type InboundMessage, type InboundWebhookPayload, type ListAudienceContactsRequest, type ListAudienceContactsResponse, type ListAudiencesRequest, type ListAudiencesResponse, type ListCampaignsRequest, type ListCampaignsResponse, type ListContactsRequest, type ListContactsResponse, type ListDomainsRequest, type ListDomainsResponse, type ListEmailsRequest, type ListEmailsResponse, type ListEventOccurrencesRequest, type ListEventOccurrencesResponse, type ListEventsRequest, type ListEventsResponse, type ListInboundMessagesRequest, type ListInboundMessagesResponse, type ListMailboxesRequest, type ListMailboxesResponse, type ListSendersRequest, type ListSendersResponse, type ListSequenceEntriesRequest, type ListSequenceEntriesResponse, type ListSequencesRequest, type ListSequencesResponse, type ListTemplatesRequest, type ListTemplatesResponse, Mail, type MailContact, type MailEvent, type Mailbox, type MailboxWithSecret, Mailboxes, type PauseCampaignResponse, type PauseSequenceResponse, type PreviewTemplateRequest, type PreviewTemplateResponse, type PublishSequenceResponse, type RemoveContactFromSequenceRequest, type RemoveContactFromSequenceResponse, type RemoveContactsFromAudienceRequest, type RemoveContactsFromAudienceResponse, type ResendEmailResponse, type ResumeSequenceResponse, type RotateMailboxSecretResponse, type SendBatchEmailRequest, type SendBatchEmailResponse, type SendBroadcastEmailRequest, type SendBroadcastEmailResponse, type SendCampaignRequest, type SendCampaignResponse, type SendEmailRequest, type SendEmailResponse, type Sender, type Sequence, type SequenceAnalyticsResponse, type SequenceConnection, type SequenceEntry, type SequenceEntryStatus, type SequenceNode, type SequenceNodeType, type SequenceStatus, type SequenceTriggerFrequency, type SequenceTriggerType, type SequenceWithNodes, Sequences, type SetDefaultDomainResponse, type SetNodeBranchRequest, type SetNodeEmailRequest, type SetNodeExperimentRequest, type SetNodeFilterRequest, type SetNodeTimerRequest, type Template, Templates, type TimeSeriesAnalyticsRequest, type TimeSeriesAnalyticsResponse, type TimeSeriesDataPoint, type TrackEventRequest, type TrackEventResponse, type UpdateAudienceRequest, type UpdateCampaignRequest, type UpdateContactRequest, type UpdateEventRequest, type UpdateMailboxRequest, type UpdateNodePositionRequest, type UpdateNodeRequest, type UpdateSequenceRequest, type UpdateTemplateRequest, type VerifyDomainResponse, type VerifyInboundWebhookOptions, WebhookVerificationError, verifyInboundWebhook };
@@ -430,11 +430,19 @@ var Mailboxes = class {
430
430
  }
431
431
  http;
432
432
  /**
433
- * Create a new mailbox
433
+ * Create a new mailbox. The result carries `webhookSecret`, which signs every
434
+ * inbound delivery; store it now (only `create` and `rotateSecret` return it).
434
435
  */
435
436
  async create(request) {
436
437
  return this.http.post("/mail/mailboxes", request);
437
438
  }
439
+ /**
440
+ * Replace a mailbox's webhook secret and return the new one. Deliveries are
441
+ * signed with the new secret from then on.
442
+ */
443
+ async rotateSecret(id) {
444
+ return this.http.post(`/mail/mailboxes/${id}/rotate-secret`, {});
445
+ }
438
446
  /**
439
447
  * Get a mailbox by ID
440
448
  */
@@ -968,6 +976,92 @@ var Mail = class {
968
976
  }
969
977
  };
970
978
 
979
+ // src/mail/webhooks.ts
980
+ var WebhookVerificationError = class extends Error {
981
+ constructor(message) {
982
+ super(message);
983
+ this.name = "WebhookVerificationError";
984
+ }
985
+ };
986
+ function header(headers, name) {
987
+ if (typeof headers.get === "function") return headers.get(name) ?? void 0;
988
+ const record = headers;
989
+ for (const key of Object.keys(record)) {
990
+ if (key.toLowerCase() === name) {
991
+ const v = record[key];
992
+ return Array.isArray(v) ? v[0] : v;
993
+ }
994
+ }
995
+ return void 0;
996
+ }
997
+ function base64ToBytes(b64) {
998
+ const bin = atob(b64);
999
+ const out = new Uint8Array(bin.length);
1000
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
1001
+ return out;
1002
+ }
1003
+ function bytesToBase64(bytes) {
1004
+ let bin = "";
1005
+ for (const b of bytes) bin += String.fromCharCode(b);
1006
+ return btoa(bin);
1007
+ }
1008
+ async function subtle() {
1009
+ const global = globalThis.crypto;
1010
+ if (global?.subtle) return global.subtle;
1011
+ const specifier = "node:crypto";
1012
+ const nodeCrypto = await import(
1013
+ /* webpackIgnore: true */
1014
+ /* @vite-ignore */
1015
+ specifier
1016
+ );
1017
+ return nodeCrypto.webcrypto.subtle;
1018
+ }
1019
+ function timingSafeEqual(a, b) {
1020
+ if (a.length !== b.length) return false;
1021
+ let diff = 0;
1022
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
1023
+ return diff === 0;
1024
+ }
1025
+ async function verifyInboundWebhook(options) {
1026
+ const id = header(options.headers, "webhook-id");
1027
+ const timestamp = header(options.headers, "webhook-timestamp");
1028
+ const signatures = header(options.headers, "webhook-signature");
1029
+ if (!id || !timestamp || !signatures) {
1030
+ throw new WebhookVerificationError("missing webhook-id, webhook-timestamp, or webhook-signature header");
1031
+ }
1032
+ const seconds = Number(timestamp);
1033
+ if (!Number.isInteger(seconds)) throw new WebhookVerificationError("webhook-timestamp is not a whole number");
1034
+ const now = Math.floor((options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
1035
+ const tolerance = options.toleranceSeconds ?? 300;
1036
+ if (Math.abs(now - seconds) > tolerance) {
1037
+ throw new WebhookVerificationError(`webhook-timestamp is more than ${tolerance} seconds from now`);
1038
+ }
1039
+ const body = typeof options.payload === "string" ? options.payload : new TextDecoder().decode(options.payload instanceof ArrayBuffer ? new Uint8Array(options.payload) : options.payload);
1040
+ const secret = options.secret.startsWith("whsec_") ? options.secret.slice(6) : options.secret;
1041
+ const webcrypto = await subtle();
1042
+ const key = await webcrypto.importKey(
1043
+ "raw",
1044
+ base64ToBytes(secret),
1045
+ { name: "HMAC", hash: "SHA-256" },
1046
+ false,
1047
+ ["sign"]
1048
+ );
1049
+ const mac = new Uint8Array(
1050
+ await webcrypto.sign("HMAC", key, new TextEncoder().encode(`${id}.${seconds}.${body}`))
1051
+ );
1052
+ const expected = bytesToBase64(mac);
1053
+ const matched = signatures.split(" ").map((s) => s.split(",", 2)).some(([version, sig]) => version === "v1" && sig !== void 0 && timingSafeEqual(sig, expected));
1054
+ if (!matched) throw new WebhookVerificationError("no webhook-signature matches");
1055
+ let payload;
1056
+ try {
1057
+ payload = JSON.parse(body);
1058
+ } catch {
1059
+ throw new WebhookVerificationError("the payload is not JSON");
1060
+ }
1061
+ if (payload.id !== id) throw new WebhookVerificationError("the payload id does not match webhook-id");
1062
+ return payload;
1063
+ }
1064
+
971
1065
  exports.Audiences = Audiences;
972
1066
  exports.Campaigns = Campaigns;
973
1067
  exports.Contacts = Contacts;
@@ -977,5 +1071,7 @@ exports.Mail = Mail;
977
1071
  exports.Mailboxes = Mailboxes;
978
1072
  exports.Sequences = Sequences;
979
1073
  exports.Templates = Templates;
1074
+ exports.WebhookVerificationError = WebhookVerificationError;
1075
+ exports.verifyInboundWebhook = verifyInboundWebhook;
980
1076
  //# sourceMappingURL=index.js.map
981
1077
  //# sourceMappingURL=index.js.map