@stack0/sdk 0.5.18 → 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.
- package/dist/cdn/index.d.mts +188 -1
- package/dist/cdn/index.d.ts +188 -1
- package/dist/cdn/index.js +107 -0
- package/dist/cdn/index.js.map +1 -1
- package/dist/cdn/index.mjs +107 -0
- package/dist/cdn/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +204 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +203 -2
- package/dist/index.mjs.map +1 -1
- package/dist/mail/index.d.mts +76 -4
- package/dist/mail/index.d.ts +76 -4
- package/dist/mail/index.js +97 -1
- package/dist/mail/index.js.map +1 -1
- package/dist/mail/index.mjs +96 -2
- package/dist/mail/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/mail/index.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/mail/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/mail/index.js
CHANGED
|
@@ -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
|