@stackshift-cloud/sdk 1.3.1 → 1.4.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/README.md +68 -0
- package/dist/asset-media-v2.d.ts +15 -1
- package/dist/asset-workflows.d.ts +5 -13
- package/dist/assets-dam-types.d.ts +35 -0
- package/dist/assets.d.ts +11 -2
- package/dist/assets.js +22 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +5 -1
- package/dist/generated/assets-contract.d.ts +41 -6
- package/dist/generated/assets-contract.js +8 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +7 -0
- package/dist/mail-billing.d.ts +12 -0
- package/dist/mail-billing.js +3 -0
- package/dist/mail-brands.d.ts +14 -1
- package/dist/mail-campaigns.d.ts +6 -3
- package/dist/mail-campaigns.js +6 -4
- package/dist/mail-delivery-types.d.ts +1 -0
- package/dist/mail-events-webhooks.d.ts +8 -3
- package/dist/mail-events-webhooks.js +13 -0
- package/dist/mail-mailboxes.d.ts +358 -0
- package/dist/mail-mailboxes.js +202 -0
- package/dist/mail-webhook-types.d.ts +40 -1
- package/dist/mail.d.ts +8 -0
- package/dist/mail.js +15 -0
- package/dist/partner.d.ts +369 -0
- package/dist/partner.js +70 -0
- package/dist/support-action-manifest.d.ts +3 -0
- package/dist/support-action-manifest.js +40 -0
- package/dist/support-action-receiver.d.ts +20 -0
- package/dist/support-action-receiver.js +37 -0
- package/dist/support-action-sqlite.d.ts +17 -0
- package/dist/support-action-sqlite.js +30 -0
- package/dist/support-action-types.d.ts +93 -0
- package/dist/support-action-types.js +1 -0
- package/dist/support-actions.d.ts +19 -0
- package/dist/support-actions.js +213 -0
- package/dist/types.d.ts +5 -0
- package/package.json +3 -2
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { MailMode } from './mail-simulation.js';
|
|
2
2
|
import type { JsonValue } from './types.js';
|
|
3
|
-
export type MailEventType = 'mail.message.opened' | 'mail.message.clicked' | 'mail.message.accepted' | 'mail.message.queued' | 'mail.message.sending' | 'mail.message.mta_accepted' | 'mail.delivery.delayed' | 'mail.delivery.delivered' | 'mail.message.failed' | 'mail.message.bounced' | 'mail.message.suppressed' | 'mail.message.complained' | 'mail.suppression.created' | 'mail.suppression.deleted' | 'mail.otp.created' | 'mail.otp.sent' | 'mail.otp.verified' | 'mail.otp.failed_attempt' | 'mail.otp.failed' | 'mail.otp.expired' | 'mail.otp.canceled' | 'mail.template.created' | 'mail.template.updated' | 'mail.template.deleted' | 'mail.template.version.created' | 'mail.template.test_queued' | 'mail.domain.created' | 'mail.domain.verified' | 'mail.domain.failed' | 'mail.domain.deleted' | 'mail.webhook.created' | 'mail.webhook.updated' | 'mail.webhook.disabled' | 'mail.webhook.delivery.succeeded' | 'mail.webhook.delivery.failed' | 'mail.audience.created' | 'mail.audience.members.imported' | 'mail.campaign.created' | 'mail.campaign.queued';
|
|
3
|
+
export type MailEventType = 'mail.message.opened' | 'mail.message.clicked' | 'mail.message.accepted' | 'mail.message.queued' | 'mail.message.sending' | 'mail.message.mta_accepted' | 'mail.delivery.delayed' | 'mail.delivery.delivered' | 'mail.message.failed' | 'mail.message.bounced' | 'mail.message.suppressed' | 'mail.message.complained' | 'mail.suppression.created' | 'mail.suppression.deleted' | 'mail.otp.created' | 'mail.otp.sent' | 'mail.otp.verified' | 'mail.otp.failed_attempt' | 'mail.otp.failed' | 'mail.otp.expired' | 'mail.otp.canceled' | 'mail.template.created' | 'mail.template.updated' | 'mail.template.deleted' | 'mail.template.version.created' | 'mail.template.test_queued' | 'mail.domain.created' | 'mail.domain.verified' | 'mail.domain.failed' | 'mail.domain.deleted' | 'mail.webhook.created' | 'mail.webhook.updated' | 'mail.webhook.disabled' | 'mail.webhook.delivery.succeeded' | 'mail.webhook.delivery.failed' | 'mail.audience.created' | 'mail.audience.members.imported' | 'mail.campaign.created' | 'mail.campaign.queued' | 'mail.campaign.stopped' | 'partner.domain.registered' | 'partner.domain.failed' | 'partner.domain.dns_verified' | 'partner.brand.ready' | 'partner.warmup.progress' | 'partner.brand.warning' | 'partner.brand.paused' | 'partner.brand.resumed' | 'partner.brand.bounced' | 'partner.brand.complained' | 'partner.domain.renewal_due' | 'partner.domain.renewed' | 'partner.domain.renewal_failed';
|
|
4
|
+
export interface MailCampaignStoppedEvent {
|
|
5
|
+
campaignId: string;
|
|
6
|
+
brandId: string;
|
|
7
|
+
reason: 'sender_paused';
|
|
8
|
+
pauseReason: 'complaint_rate' | 'bounce_rate' | 'partner_suspended' | string;
|
|
9
|
+
pausedSince: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
4
12
|
export interface MailEvent {
|
|
5
13
|
mode?: MailMode;
|
|
6
14
|
id: string;
|
|
@@ -43,6 +51,20 @@ export interface MailWebhook {
|
|
|
43
51
|
lastFailureAt?: string | null;
|
|
44
52
|
createdAt: string;
|
|
45
53
|
}
|
|
54
|
+
export interface MailWebhookWaitingHighlight {
|
|
55
|
+
type: string;
|
|
56
|
+
shop: string;
|
|
57
|
+
}
|
|
58
|
+
export interface MailWebhookWaitingSummary {
|
|
59
|
+
count: number;
|
|
60
|
+
capped: boolean;
|
|
61
|
+
oldestAt: string;
|
|
62
|
+
merchantUpdates: number;
|
|
63
|
+
highlights: MailWebhookWaitingHighlight[];
|
|
64
|
+
}
|
|
65
|
+
export interface MailWebhookDetail extends MailWebhook {
|
|
66
|
+
waitingSummary: MailWebhookWaitingSummary | null;
|
|
67
|
+
}
|
|
46
68
|
export interface MailWebhookCreateInput {
|
|
47
69
|
url: string;
|
|
48
70
|
description?: string;
|
|
@@ -80,6 +102,23 @@ export interface MailWebhookDelivery {
|
|
|
80
102
|
createdAt: string;
|
|
81
103
|
completedAt?: string | null;
|
|
82
104
|
}
|
|
105
|
+
export interface MailWebhookDeliveryMerchant {
|
|
106
|
+
partnerDomainId: string;
|
|
107
|
+
merchantRef: string;
|
|
108
|
+
shop: string;
|
|
109
|
+
}
|
|
110
|
+
export interface MailWebhookDeliveryListItem extends MailWebhookDelivery {
|
|
111
|
+
durationMs: number | null;
|
|
112
|
+
merchant: MailWebhookDeliveryMerchant | null;
|
|
113
|
+
}
|
|
114
|
+
export interface MailWebhookDiagnosis {
|
|
115
|
+
checkedAt: string;
|
|
116
|
+
repliesRead: number;
|
|
117
|
+
summary: string;
|
|
118
|
+
}
|
|
119
|
+
export interface MailWebhookDiagnosisResult {
|
|
120
|
+
diagnosis: MailWebhookDiagnosis | null;
|
|
121
|
+
}
|
|
83
122
|
export interface MailWebhookDeliveryAttempt {
|
|
84
123
|
id: string;
|
|
85
124
|
attemptNumber: number;
|
package/dist/mail.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { MailAudiencesClient, MailCampaignsClient } from './mail-campaigns.js';
|
|
|
19
19
|
export { MailAudiencesClient, MailCampaignsClient } from './mail-campaigns.js';
|
|
20
20
|
import { MailExportsClient } from './mail-exports.js';
|
|
21
21
|
export { MailExportsClient } from './mail-exports.js';
|
|
22
|
+
import { MailboxAliasesClient, MailboxDomainsClient, MailboxGroupsClient, MailboxImportsClient, MailboxRoutesClient, MailboxesClient } from './mail-mailboxes.js';
|
|
23
|
+
export { MailboxAliasesClient, MailboxDomainsClient, MailboxGroupsClient, MailboxImportsClient, MailboxRoutesClient, MailboxesClient } from './mail-mailboxes.js';
|
|
22
24
|
import type { MailTemplateDiagnosticInput, MailTemplateDiagnosticResult } from './mail-diagnostics.js';
|
|
23
25
|
export declare class MailClient {
|
|
24
26
|
private readonly client;
|
|
@@ -41,6 +43,12 @@ export declare class MailClient {
|
|
|
41
43
|
readonly audiences: MailAudiencesClient;
|
|
42
44
|
readonly campaigns: MailCampaignsClient;
|
|
43
45
|
readonly exports: MailExportsClient;
|
|
46
|
+
readonly mailboxDomains: MailboxDomainsClient;
|
|
47
|
+
readonly mailboxes: MailboxesClient;
|
|
48
|
+
readonly aliases: MailboxAliasesClient;
|
|
49
|
+
readonly groups: MailboxGroupsClient;
|
|
50
|
+
readonly routes: MailboxRoutesClient;
|
|
51
|
+
readonly mailboxImports: MailboxImportsClient;
|
|
44
52
|
readonly limits: MailLimitsClient;
|
|
45
53
|
readonly reputation: MailReputationClient;
|
|
46
54
|
readonly scheduled: MailScheduledClient;
|
package/dist/mail.js
CHANGED
|
@@ -13,6 +13,8 @@ import { MailAudiencesClient, MailCampaignsClient } from './mail-campaigns.js';
|
|
|
13
13
|
export { MailAudiencesClient, MailCampaignsClient } from './mail-campaigns.js';
|
|
14
14
|
import { MailExportsClient } from './mail-exports.js';
|
|
15
15
|
export { MailExportsClient } from './mail-exports.js';
|
|
16
|
+
import { MailboxAliasesClient, MailboxDomainsClient, MailboxGroupsClient, MailboxImportsClient, MailboxRoutesClient, MailboxesClient } from './mail-mailboxes.js';
|
|
17
|
+
export { MailboxAliasesClient, MailboxDomainsClient, MailboxGroupsClient, MailboxImportsClient, MailboxRoutesClient, MailboxesClient } from './mail-mailboxes.js';
|
|
16
18
|
export class MailClient {
|
|
17
19
|
client;
|
|
18
20
|
automations;
|
|
@@ -34,6 +36,12 @@ export class MailClient {
|
|
|
34
36
|
audiences;
|
|
35
37
|
campaigns;
|
|
36
38
|
exports;
|
|
39
|
+
mailboxDomains;
|
|
40
|
+
mailboxes;
|
|
41
|
+
aliases;
|
|
42
|
+
groups;
|
|
43
|
+
routes;
|
|
44
|
+
mailboxImports;
|
|
37
45
|
limits;
|
|
38
46
|
reputation;
|
|
39
47
|
scheduled;
|
|
@@ -61,6 +69,12 @@ export class MailClient {
|
|
|
61
69
|
this.audiences = new MailAudiencesClient(client);
|
|
62
70
|
this.campaigns = new MailCampaignsClient(client);
|
|
63
71
|
this.exports = new MailExportsClient(client);
|
|
72
|
+
this.mailboxDomains = new MailboxDomainsClient(client);
|
|
73
|
+
this.mailboxes = new MailboxesClient(client);
|
|
74
|
+
this.aliases = new MailboxAliasesClient(client);
|
|
75
|
+
this.groups = new MailboxGroupsClient(client);
|
|
76
|
+
this.routes = new MailboxRoutesClient(client);
|
|
77
|
+
this.mailboxImports = new MailboxImportsClient(client);
|
|
64
78
|
this.limits = new MailLimitsClient(client);
|
|
65
79
|
this.reputation = new MailReputationClient(client);
|
|
66
80
|
this.scheduled = new MailScheduledClient(client);
|
|
@@ -248,6 +262,7 @@ export class MailSuppressionsClient {
|
|
|
248
262
|
const query = new URLSearchParams();
|
|
249
263
|
append(query, 'email', options.email);
|
|
250
264
|
append(query, 'reason', options.reason);
|
|
265
|
+
append(query, 'brandId', options.brandId);
|
|
251
266
|
append(query, 'limit', options.limit);
|
|
252
267
|
append(query, 'cursor', options.cursor);
|
|
253
268
|
const suffix = query.toString();
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import type { StackShift } from './client.js';
|
|
2
|
+
import type { MailBrand } from './mail-brands.js';
|
|
3
|
+
import type { JsonValue } from './types.js';
|
|
4
|
+
export type PartnerDomainState = 'registering' | 'mail_setup' | 'verifying' | 'replies' | 'brand_setup' | 'ready' | 'failed';
|
|
5
|
+
export type PartnerRepliesMode = 'forward' | 'mailbox';
|
|
6
|
+
export interface PartnerAccount {
|
|
7
|
+
userId: string;
|
|
8
|
+
name: string;
|
|
9
|
+
status: 'active' | 'suspended' | string;
|
|
10
|
+
complaintPauseRate: number;
|
|
11
|
+
bouncePauseRate: number;
|
|
12
|
+
dedicatedPoolName: string | null;
|
|
13
|
+
dailyPurchaseLimit: number;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PartnerWalletBalance {
|
|
18
|
+
currency: string;
|
|
19
|
+
balance_minor: number;
|
|
20
|
+
held_minor: number;
|
|
21
|
+
disputed_minor: number;
|
|
22
|
+
available_minor: number;
|
|
23
|
+
}
|
|
24
|
+
export interface PartnerLimits {
|
|
25
|
+
maxComplaintPauseRate: number;
|
|
26
|
+
maxBouncePauseRate: number;
|
|
27
|
+
minimumRecipients: number;
|
|
28
|
+
windowDays: number;
|
|
29
|
+
shortWindowHours: number;
|
|
30
|
+
dailyPurchaseLimit: number;
|
|
31
|
+
warningFraction: number;
|
|
32
|
+
merchantSharePercent: number;
|
|
33
|
+
merchantCount: number;
|
|
34
|
+
dailySendingLimit: number;
|
|
35
|
+
hourlySendingLimit: number;
|
|
36
|
+
}
|
|
37
|
+
export interface PartnerOverview {
|
|
38
|
+
account: PartnerAccount;
|
|
39
|
+
wallet: PartnerWalletBalance[];
|
|
40
|
+
webhookEvents: string[];
|
|
41
|
+
limits: PartnerLimits;
|
|
42
|
+
}
|
|
43
|
+
export interface PartnerSettingsInput {
|
|
44
|
+
complaintPauseRate: number;
|
|
45
|
+
bouncePauseRate: number;
|
|
46
|
+
}
|
|
47
|
+
export interface PartnerRegistrant {
|
|
48
|
+
first_name: string;
|
|
49
|
+
last_name: string;
|
|
50
|
+
organization?: string;
|
|
51
|
+
email: string;
|
|
52
|
+
phone: string;
|
|
53
|
+
address: string;
|
|
54
|
+
city: string;
|
|
55
|
+
state: string;
|
|
56
|
+
postal_code: string;
|
|
57
|
+
country: string;
|
|
58
|
+
}
|
|
59
|
+
export interface PartnerBrandInput {
|
|
60
|
+
name: string;
|
|
61
|
+
fromLocalPart?: string;
|
|
62
|
+
postalAddress: string;
|
|
63
|
+
website?: string;
|
|
64
|
+
supportEmail?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface PartnerRepliesInput {
|
|
67
|
+
mode: PartnerRepliesMode;
|
|
68
|
+
forwardTo?: string;
|
|
69
|
+
mailboxOwnerEmail?: string;
|
|
70
|
+
mailboxPlan?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface PartnerDomainPurchaseInput {
|
|
73
|
+
domain: string;
|
|
74
|
+
years: number;
|
|
75
|
+
currency?: 'NGN' | 'USD';
|
|
76
|
+
merchantRef: string;
|
|
77
|
+
registrant: PartnerRegistrant;
|
|
78
|
+
brand: PartnerBrandInput;
|
|
79
|
+
replies: PartnerRepliesInput;
|
|
80
|
+
}
|
|
81
|
+
export interface PartnerPurchaseOptions {
|
|
82
|
+
idempotencyKey: string;
|
|
83
|
+
}
|
|
84
|
+
export type PartnerFailureCode = 'nameservers_changed' | 'dns_not_visible' | 'wallet_short' | 'registry_taken' | 'registration_rejected' | 'registration_timed_out' | 'purchase_interrupted' | 'mailbox_unavailable' | 'mail_setup_rejected' | 'dns_provider_error' | 'registration_mismatch' | 'sending_domains_missing' | 'internal_retrying';
|
|
85
|
+
export interface PartnerFailureFact {
|
|
86
|
+
label: string;
|
|
87
|
+
value: string;
|
|
88
|
+
tone: 'bad' | 'ok' | 'neutral';
|
|
89
|
+
at: string | null;
|
|
90
|
+
}
|
|
91
|
+
export type PartnerFailureFix = {
|
|
92
|
+
label: string;
|
|
93
|
+
action: 'restore_nameservers';
|
|
94
|
+
} | {
|
|
95
|
+
label: string;
|
|
96
|
+
href: string;
|
|
97
|
+
};
|
|
98
|
+
export interface PartnerFailure {
|
|
99
|
+
code: PartnerFailureCode;
|
|
100
|
+
title: string;
|
|
101
|
+
headline: string;
|
|
102
|
+
explanation: string;
|
|
103
|
+
since: string | null;
|
|
104
|
+
facts: PartnerFailureFact[];
|
|
105
|
+
data: Record<string, JsonValue>;
|
|
106
|
+
fix: PartnerFailureFix | null;
|
|
107
|
+
canRetry: boolean;
|
|
108
|
+
}
|
|
109
|
+
export interface PartnerDiagnosis {
|
|
110
|
+
checkedAt: string;
|
|
111
|
+
summary: string;
|
|
112
|
+
}
|
|
113
|
+
export interface PartnerPause {
|
|
114
|
+
reason: 'complaint_rate' | 'bounce_rate';
|
|
115
|
+
rate: number;
|
|
116
|
+
limit: number;
|
|
117
|
+
window: 'last_7_days' | 'last_24_hours';
|
|
118
|
+
campaignId: string | null;
|
|
119
|
+
campaignName: string | null;
|
|
120
|
+
listId: string | null;
|
|
121
|
+
listName: string | null;
|
|
122
|
+
listImportedAt: string | null;
|
|
123
|
+
listShare: number | null;
|
|
124
|
+
recoversAt: string | null;
|
|
125
|
+
}
|
|
126
|
+
export interface PartnerWaitingFor {
|
|
127
|
+
kind: 'wallet';
|
|
128
|
+
currency: string;
|
|
129
|
+
requiredMinor: number;
|
|
130
|
+
availableMinor: number;
|
|
131
|
+
}
|
|
132
|
+
export interface PartnerRefund {
|
|
133
|
+
amountMinor: number;
|
|
134
|
+
currency: string;
|
|
135
|
+
at: string;
|
|
136
|
+
}
|
|
137
|
+
export interface PartnerMerchantDomain {
|
|
138
|
+
id: string;
|
|
139
|
+
partnerUserId: string;
|
|
140
|
+
merchantRef: string;
|
|
141
|
+
domain: string;
|
|
142
|
+
years: number;
|
|
143
|
+
domainOrderId: string | null;
|
|
144
|
+
registeredDomainId: string | null;
|
|
145
|
+
mailDomainId: string | null;
|
|
146
|
+
brandId: string | null;
|
|
147
|
+
mailboxDomainId: string | null;
|
|
148
|
+
mailboxId: string | null;
|
|
149
|
+
brand: PartnerBrandInput;
|
|
150
|
+
registrant: PartnerRegistrant;
|
|
151
|
+
replies: PartnerRepliesInput;
|
|
152
|
+
state: PartnerDomainState;
|
|
153
|
+
failedState: PartnerDomainState | null;
|
|
154
|
+
lastError: string;
|
|
155
|
+
attempts: number;
|
|
156
|
+
nextAttemptAt: string;
|
|
157
|
+
lastAttemptAt: string | null;
|
|
158
|
+
waitingFor: PartnerWaitingFor | null;
|
|
159
|
+
warmupDay: number;
|
|
160
|
+
healthWarnedAt: string | null;
|
|
161
|
+
transferredOutAt: string | null;
|
|
162
|
+
amountMinor: number;
|
|
163
|
+
currency: string;
|
|
164
|
+
walletReference: string;
|
|
165
|
+
createdAt: string;
|
|
166
|
+
updatedAt: string;
|
|
167
|
+
expiryDate: string | null;
|
|
168
|
+
brandPausedAt: string | null;
|
|
169
|
+
brandPauseReason: 'complaint_rate' | 'bounce_rate' | '';
|
|
170
|
+
pause: PartnerPause | null;
|
|
171
|
+
failure: PartnerFailure | null;
|
|
172
|
+
diagnosis: PartnerDiagnosis | null;
|
|
173
|
+
}
|
|
174
|
+
export interface PartnerDomainListOptions {
|
|
175
|
+
merchantRef?: string;
|
|
176
|
+
q?: string;
|
|
177
|
+
attention?: boolean;
|
|
178
|
+
limit?: number;
|
|
179
|
+
offset?: number;
|
|
180
|
+
}
|
|
181
|
+
export interface PartnerWarmupSummary {
|
|
182
|
+
status: string;
|
|
183
|
+
day: number;
|
|
184
|
+
totalDays: number;
|
|
185
|
+
marketingUnlocked: boolean;
|
|
186
|
+
marketingOpensInDays: number;
|
|
187
|
+
}
|
|
188
|
+
export interface PartnerDomainListItem extends PartnerMerchantDomain {
|
|
189
|
+
health: PartnerBrandHealth | null;
|
|
190
|
+
transactionalHealth: PartnerBrandHealth | null;
|
|
191
|
+
refunded: boolean;
|
|
192
|
+
sentByDay: number[];
|
|
193
|
+
warmup: PartnerWarmupSummary | null;
|
|
194
|
+
}
|
|
195
|
+
export interface PartnerDomainList {
|
|
196
|
+
domains: PartnerDomainListItem[];
|
|
197
|
+
total: number;
|
|
198
|
+
attentionTotal: number;
|
|
199
|
+
}
|
|
200
|
+
export interface PartnerSenders {
|
|
201
|
+
marketing: string;
|
|
202
|
+
transactional: string;
|
|
203
|
+
replyTo: string;
|
|
204
|
+
}
|
|
205
|
+
export interface PartnerHealthWindow {
|
|
206
|
+
attempted: number;
|
|
207
|
+
accepted: number;
|
|
208
|
+
bounced: number;
|
|
209
|
+
complained: number;
|
|
210
|
+
bounceRate: number;
|
|
211
|
+
complaintRate: number;
|
|
212
|
+
}
|
|
213
|
+
export interface PartnerBrandHealth extends PartnerHealthWindow {
|
|
214
|
+
last24h: PartnerHealthWindow;
|
|
215
|
+
}
|
|
216
|
+
export interface PartnerWarmup {
|
|
217
|
+
status: string;
|
|
218
|
+
day: number;
|
|
219
|
+
totalDays: number;
|
|
220
|
+
dailyLimit: number;
|
|
221
|
+
marketingDailyLimit: number;
|
|
222
|
+
marketingUnlocked: boolean;
|
|
223
|
+
cleanTransactionalDays: number;
|
|
224
|
+
requiredCleanDays: number;
|
|
225
|
+
firstCampaignCap: number;
|
|
226
|
+
paused: boolean;
|
|
227
|
+
marketingSentToday: number;
|
|
228
|
+
marketingOpensInDays: number;
|
|
229
|
+
}
|
|
230
|
+
export type PartnerRecordPurpose = 'dkim' | 'spf' | 'return_path' | 'dmarc' | 'reply_mx' | 'reply_ownership' | 'mta_sts' | 'mta_sts_policy' | 'tls_reporting';
|
|
231
|
+
export interface PartnerRecord {
|
|
232
|
+
domain: string;
|
|
233
|
+
type: string;
|
|
234
|
+
name: string;
|
|
235
|
+
value: string;
|
|
236
|
+
priority: number | null;
|
|
237
|
+
verified: boolean;
|
|
238
|
+
purpose: PartnerRecordPurpose;
|
|
239
|
+
}
|
|
240
|
+
export interface PartnerDomainView extends PartnerMerchantDomain {
|
|
241
|
+
senders: PartnerSenders;
|
|
242
|
+
brandStatus: MailBrand | null;
|
|
243
|
+
health: PartnerBrandHealth | null;
|
|
244
|
+
transactionalHealth: PartnerBrandHealth | null;
|
|
245
|
+
warmup: PartnerWarmup | null;
|
|
246
|
+
replies: PartnerRepliesInput & {
|
|
247
|
+
mailboxOwnerName: string | null;
|
|
248
|
+
};
|
|
249
|
+
records: PartnerRecord[];
|
|
250
|
+
refunded: boolean;
|
|
251
|
+
refund: PartnerRefund | null;
|
|
252
|
+
webhookDeliveredAt: string | null;
|
|
253
|
+
}
|
|
254
|
+
export interface PartnerTransferOut {
|
|
255
|
+
domain: string;
|
|
256
|
+
authCode: string;
|
|
257
|
+
}
|
|
258
|
+
export interface PartnerUsageOptions {
|
|
259
|
+
from?: string;
|
|
260
|
+
to?: string;
|
|
261
|
+
}
|
|
262
|
+
export interface PartnerBrandUsage {
|
|
263
|
+
partnerDomainId: string;
|
|
264
|
+
merchantRef: string;
|
|
265
|
+
domain: string;
|
|
266
|
+
brandId: string | null;
|
|
267
|
+
brandName: string;
|
|
268
|
+
marketingRecipients: number;
|
|
269
|
+
transactionalRecipients: number;
|
|
270
|
+
domainChargesMinor: number;
|
|
271
|
+
refundedMinor: number;
|
|
272
|
+
currency: string;
|
|
273
|
+
}
|
|
274
|
+
export interface PartnerUsage {
|
|
275
|
+
from: string;
|
|
276
|
+
to: string;
|
|
277
|
+
brands: PartnerBrandUsage[];
|
|
278
|
+
}
|
|
279
|
+
export interface PartnerComplaintSummary {
|
|
280
|
+
complained: number;
|
|
281
|
+
suppressed: number;
|
|
282
|
+
}
|
|
283
|
+
export interface PartnerComplaintSource {
|
|
284
|
+
audienceId: string;
|
|
285
|
+
audienceName: string;
|
|
286
|
+
importedAt: string | null;
|
|
287
|
+
contacts: number;
|
|
288
|
+
neverOpened: number;
|
|
289
|
+
complained: number;
|
|
290
|
+
}
|
|
291
|
+
export interface PartnerBulkSuppressionInput {
|
|
292
|
+
audienceId: string;
|
|
293
|
+
reason: 'never_opened';
|
|
294
|
+
}
|
|
295
|
+
export interface PartnerBulkSuppression {
|
|
296
|
+
id: string;
|
|
297
|
+
audienceId: string;
|
|
298
|
+
reason: 'never_opened';
|
|
299
|
+
suppressed: number;
|
|
300
|
+
}
|
|
301
|
+
export type PartnerRequestKind = 'purchase_limit' | 'dedicated_addresses';
|
|
302
|
+
export interface PartnerRequestInput {
|
|
303
|
+
kind: PartnerRequestKind;
|
|
304
|
+
message?: string;
|
|
305
|
+
}
|
|
306
|
+
export interface PartnerRequest {
|
|
307
|
+
id: string;
|
|
308
|
+
kind: PartnerRequestKind;
|
|
309
|
+
message: string;
|
|
310
|
+
status: 'open' | 'done' | 'declined';
|
|
311
|
+
resolvedAt: string | null;
|
|
312
|
+
createdAt: string;
|
|
313
|
+
updatedAt: string;
|
|
314
|
+
}
|
|
315
|
+
export interface PartnerEventBase {
|
|
316
|
+
partnerDomainId: string;
|
|
317
|
+
merchantRef: string;
|
|
318
|
+
domain: string;
|
|
319
|
+
state: PartnerDomainState;
|
|
320
|
+
brandId?: string;
|
|
321
|
+
}
|
|
322
|
+
export interface PartnerBrandHealthEvent extends PartnerEventBase {
|
|
323
|
+
bounceRate: number;
|
|
324
|
+
complaintRate: number;
|
|
325
|
+
recipients: number;
|
|
326
|
+
last24h: PartnerHealthWindow;
|
|
327
|
+
bounceLimit: number;
|
|
328
|
+
complaintLimit: number;
|
|
329
|
+
reason?: 'complaint_rate' | 'bounce_rate';
|
|
330
|
+
}
|
|
331
|
+
export interface PartnerDomainRenewedEvent extends PartnerEventBase {
|
|
332
|
+
expiryDate: string;
|
|
333
|
+
previousExpiryDate: string;
|
|
334
|
+
}
|
|
335
|
+
export interface PartnerDomainRenewalDueEvent extends PartnerEventBase {
|
|
336
|
+
expiryDate: string;
|
|
337
|
+
autoRenew: boolean;
|
|
338
|
+
}
|
|
339
|
+
export interface PartnerDomainRenewalFailedEvent extends PartnerEventBase {
|
|
340
|
+
expiryDate: string;
|
|
341
|
+
reason: string;
|
|
342
|
+
amountMinor: number;
|
|
343
|
+
currency: string;
|
|
344
|
+
}
|
|
345
|
+
export declare class PartnerClient {
|
|
346
|
+
private readonly client;
|
|
347
|
+
constructor(client: StackShift);
|
|
348
|
+
overview(): Promise<PartnerOverview>;
|
|
349
|
+
updateSettings(input: PartnerSettingsInput): Promise<PartnerAccount>;
|
|
350
|
+
purchaseDomain(input: PartnerDomainPurchaseInput, { idempotencyKey }: PartnerPurchaseOptions): Promise<PartnerMerchantDomain>;
|
|
351
|
+
listDomains(options?: PartnerDomainListOptions): Promise<PartnerDomainList>;
|
|
352
|
+
getDomain(id: string): Promise<PartnerDomainView>;
|
|
353
|
+
retryDomain(id: string): Promise<PartnerMerchantDomain>;
|
|
354
|
+
transferOut(id: string): Promise<PartnerTransferOut>;
|
|
355
|
+
getDiagnosis(id: string): Promise<PartnerDiagnosis>;
|
|
356
|
+
restoreDns(id: string): Promise<PartnerMerchantDomain>;
|
|
357
|
+
resumeBrand(brandId: string): Promise<MailBrand>;
|
|
358
|
+
getComplaints(brandId: string): Promise<PartnerComplaintSummary>;
|
|
359
|
+
listComplaintSources(brandId: string): Promise<PartnerComplaintSource[]>;
|
|
360
|
+
suppressNeverOpened(brandId: string, input: PartnerBulkSuppressionInput): Promise<PartnerBulkSuppression>;
|
|
361
|
+
undoSuppression(brandId: string, bulkId: string): Promise<{
|
|
362
|
+
restored: number;
|
|
363
|
+
}>;
|
|
364
|
+
usage(options?: PartnerUsageOptions): Promise<PartnerUsage>;
|
|
365
|
+
listRequests(): Promise<{
|
|
366
|
+
requests: PartnerRequest[];
|
|
367
|
+
}>;
|
|
368
|
+
createRequest(input: PartnerRequestInput): Promise<PartnerRequest>;
|
|
369
|
+
}
|
package/dist/partner.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export class PartnerClient {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
overview() {
|
|
7
|
+
return this.client.request('/partner');
|
|
8
|
+
}
|
|
9
|
+
updateSettings(input) {
|
|
10
|
+
return this.client.request('/partner/settings', { method: 'PUT', body: input });
|
|
11
|
+
}
|
|
12
|
+
purchaseDomain(input, { idempotencyKey }) {
|
|
13
|
+
return this.client.request('/partner/domains', {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { 'Idempotency-Key': idempotencyKey },
|
|
16
|
+
body: input,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
listDomains(options = {}) {
|
|
20
|
+
return this.client.request(`/partner/domains${queryString(options)}`);
|
|
21
|
+
}
|
|
22
|
+
getDomain(id) {
|
|
23
|
+
return this.client.request(`/partner/domains/${encodeURIComponent(id)}`);
|
|
24
|
+
}
|
|
25
|
+
retryDomain(id) {
|
|
26
|
+
return this.client.request(`/partner/domains/${encodeURIComponent(id)}/retry`, { method: 'POST' });
|
|
27
|
+
}
|
|
28
|
+
transferOut(id) {
|
|
29
|
+
return this.client.request(`/partner/domains/${encodeURIComponent(id)}/transfer-out`, { method: 'POST' });
|
|
30
|
+
}
|
|
31
|
+
getDiagnosis(id) {
|
|
32
|
+
return this.client.request(`/partner/domains/${encodeURIComponent(id)}/diagnosis`);
|
|
33
|
+
}
|
|
34
|
+
restoreDns(id) {
|
|
35
|
+
return this.client.request(`/partner/domains/${encodeURIComponent(id)}/restore-dns`, { method: 'POST' });
|
|
36
|
+
}
|
|
37
|
+
resumeBrand(brandId) {
|
|
38
|
+
return this.client.request(`/partner/brands/${encodeURIComponent(brandId)}/resume`, { method: 'POST' });
|
|
39
|
+
}
|
|
40
|
+
getComplaints(brandId) {
|
|
41
|
+
return this.client.request(`/partner/brands/${encodeURIComponent(brandId)}/complaints`);
|
|
42
|
+
}
|
|
43
|
+
listComplaintSources(brandId) {
|
|
44
|
+
return this.client.request(`/partner/brands/${encodeURIComponent(brandId)}/complaints/sources`);
|
|
45
|
+
}
|
|
46
|
+
suppressNeverOpened(brandId, input) {
|
|
47
|
+
return this.client.request(`/partner/brands/${encodeURIComponent(brandId)}/suppressions/bulk`, { method: 'POST', body: input });
|
|
48
|
+
}
|
|
49
|
+
undoSuppression(brandId, bulkId) {
|
|
50
|
+
return this.client.request(`/partner/brands/${encodeURIComponent(brandId)}/suppressions/bulk/${encodeURIComponent(bulkId)}/undo`, { method: 'POST' });
|
|
51
|
+
}
|
|
52
|
+
usage(options = {}) {
|
|
53
|
+
return this.client.request(`/partner/usage${queryString(options)}`);
|
|
54
|
+
}
|
|
55
|
+
listRequests() {
|
|
56
|
+
return this.client.request('/partner/requests');
|
|
57
|
+
}
|
|
58
|
+
createRequest(input) {
|
|
59
|
+
return this.client.request('/partner/requests', { method: 'POST', body: input });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function queryString(options) {
|
|
63
|
+
const query = new URLSearchParams();
|
|
64
|
+
for (const [key, value] of Object.entries(options)) {
|
|
65
|
+
if (value !== undefined)
|
|
66
|
+
query.set(key, String(value));
|
|
67
|
+
}
|
|
68
|
+
const suffix = query.toString();
|
|
69
|
+
return suffix ? `?${suffix}` : '';
|
|
70
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SupportActionDescriptor } from './support-action-types.js';
|
|
2
|
+
/** Validate action declarations locally; server registration and destination checks remain authoritative. */
|
|
3
|
+
export declare function validateSupportActionCatalog(version: string, actions: SupportActionDescriptor[]): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { actionCheck } from './support-actions.js';
|
|
2
|
+
const reserved = new Set(['customer', 'customer_id', 'external_customer_id', 'external_id', 'contact_id', 'workspace_id', 'environment_id', 'inbox_id', 'conversation_id', 'installation_id', 'tenant_id', 'user_id', 'organization_id', 'authorization', 'scope', 'identity', 'credentials', 'secret', 'token']);
|
|
3
|
+
function endpoint(value) {
|
|
4
|
+
if (!value || value.length > 2048)
|
|
5
|
+
return false;
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(value);
|
|
8
|
+
return url.protocol === 'https:' && !!url.hostname && !url.username && !url.password && !url.search && !url.hash && !value.includes('?') && !value.includes('#');
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function fields(schema, input) {
|
|
15
|
+
actionCheck(schema && typeof schema === 'object' && !Array.isArray(schema) && Object.keys(schema).length <= 32, 'invalid_catalog');
|
|
16
|
+
for (const [name, field] of Object.entries(schema)) {
|
|
17
|
+
actionCheck(/^[a-z][a-z0-9_]{0,63}$/.test(name) && (!input || (!reserved.has(name) && !/^(customer_|tenant_|auth_)/.test(name))), 'invalid_catalog');
|
|
18
|
+
actionCheck(field && typeof field.required === 'boolean' && Object.keys(field).every(k => ['type', 'required', 'max_length', 'minimum', 'maximum', 'enum'].includes(k)), 'invalid_catalog');
|
|
19
|
+
if (field.type === 'string')
|
|
20
|
+
actionCheck(Number.isInteger(field.max_length) && field.max_length >= 1 && field.max_length <= 16000 && field.minimum === undefined && field.maximum === undefined && (!field.enum || (Array.isArray(field.enum) && field.enum.length <= 64 && new Set(field.enum).size === field.enum.length && field.enum.every(v => typeof v === 'string' && [...v].length <= field.max_length))), 'invalid_catalog');
|
|
21
|
+
else if (field.type === 'number' || field.type === 'integer')
|
|
22
|
+
actionCheck(!field.max_length && !field.enum?.length && [field.minimum, field.maximum].every(n => n === undefined || (Number.isFinite(n) && (field.type !== 'integer' || Number.isSafeInteger(n)))) && (field.minimum === undefined || field.maximum === undefined || field.minimum <= field.maximum), 'invalid_catalog');
|
|
23
|
+
else
|
|
24
|
+
actionCheck(field.type === 'boolean' && !field.max_length && !field.enum?.length && field.minimum === undefined && field.maximum === undefined, 'invalid_catalog');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Validate action declarations locally; server registration and destination checks remain authoritative. */
|
|
28
|
+
export function validateSupportActionCatalog(version, actions) {
|
|
29
|
+
actionCheck(typeof version === 'string' && version.length <= 32 && /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.test(version) && Array.isArray(actions) && actions.length >= 1 && actions.length <= 32, 'invalid_catalog');
|
|
30
|
+
const names = new Set();
|
|
31
|
+
for (const action of actions) {
|
|
32
|
+
actionCheck(/^[a-z][a-z0-9_.]{0,79}$/.test(action.name) && !names.has(action.name) && action.version === version && action.identity_required === true && ['read', 'bounded_write', 'customer_change', 'financial', 'security'].includes(action.risk) && typeof action.idempotency_supported === 'boolean' && typeof action.safe_retry === 'boolean' && Number.isInteger(action.timeout_seconds) && action.timeout_seconds >= 1 && action.timeout_seconds <= 30 && endpoint(action.endpoint), 'invalid_catalog');
|
|
33
|
+
actionCheck((action.risk === 'read' || (action.idempotency_supported && endpoint(action.reconciliation_endpoint) && !!action.verification_operation)) && (!action.safe_retry || action.idempotency_supported) && (!action.reconciliation_endpoint || endpoint(action.reconciliation_endpoint)), 'invalid_catalog');
|
|
34
|
+
fields(action.input, true);
|
|
35
|
+
fields(action.output, false);
|
|
36
|
+
names.add(action.name);
|
|
37
|
+
}
|
|
38
|
+
for (const action of actions)
|
|
39
|
+
actionCheck(!action.verification_operation || actions.some(a => a.name === action.verification_operation && a.risk === 'read'), 'invalid_catalog');
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SupportActionReceipt, SupportActionRequest, SupportActionVerification } from './support-action-types.js';
|
|
2
|
+
export interface SupportActionClaim {
|
|
3
|
+
state: 'acquired' | 'completed' | 'running' | 'unknown' | 'conflict';
|
|
4
|
+
fence?: string;
|
|
5
|
+
response?: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export interface SupportActionDurableStore {
|
|
8
|
+
claim(namespace: 'delivery' | 'execution', key: string, digest: string): Promise<SupportActionClaim>;
|
|
9
|
+
complete(namespace: 'delivery' | 'execution', key: string, fence: string, response: Uint8Array, unknown?: boolean): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export type SupportActionHandler = (request: SupportActionRequest) => Promise<Pick<SupportActionReceipt, 'state' | 'result' | 'error_code'>>;
|
|
12
|
+
export declare function receiveSupportAction(raw: Uint8Array, headers: Record<string, string>, options: {
|
|
13
|
+
verification: SupportActionVerification;
|
|
14
|
+
store: SupportActionDurableStore;
|
|
15
|
+
execute: SupportActionHandler;
|
|
16
|
+
reconcile: SupportActionHandler;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
body: Uint8Array<ArrayBufferLike>;
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ACTION_RECEIPT_CONTEXT, actionCheck, actionHeaders, encodeSupportActionReceipt, signSupportAction, validateSupportActionFields, verifySupportAction } from './support-actions.js';
|
|
2
|
+
export async function receiveSupportAction(raw, headers, options) {
|
|
3
|
+
const verification = { ...options.verification, now: options.verification.now ?? Math.floor(Date.now() / 1000) };
|
|
4
|
+
const request = await verifySupportAction(raw, headers, verification);
|
|
5
|
+
const delivery = actionHeaders(headers)['x-stackshift-delivery-id'];
|
|
6
|
+
const digest = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', new Uint8Array(raw))), b => b.toString(16).padStart(2, '0')).join('');
|
|
7
|
+
const deliveryKey = `${request.installation_id}:${delivery}`;
|
|
8
|
+
const deliveryClaim = await options.store.claim('delivery', deliveryKey, digest);
|
|
9
|
+
actionCheck(deliveryClaim.state === 'acquired' && deliveryClaim.fence, 'delivery_replay');
|
|
10
|
+
const key = [request.installation_id, request.action.name, request.action.version, request.request_id, request.mode].join(':');
|
|
11
|
+
const claim = await options.store.claim('execution', key, digest);
|
|
12
|
+
actionCheck(claim.state !== 'conflict', 'idempotency_conflict');
|
|
13
|
+
let response;
|
|
14
|
+
if ((claim.state === 'completed' || claim.state === 'unknown') && claim.response)
|
|
15
|
+
response = claim.response;
|
|
16
|
+
else if (claim.state === 'running' || claim.state === 'unknown')
|
|
17
|
+
response = encodeSupportActionReceipt(request.request_id, claim.state === 'unknown' ? 'unknown' : 'accepted', {}, claim.state === 'unknown' ? 'execution_unknown' : 'execution_in_progress');
|
|
18
|
+
else {
|
|
19
|
+
actionCheck(claim.state === 'acquired' && claim.fence, 'invalid_store_claim');
|
|
20
|
+
let state = 'unknown';
|
|
21
|
+
try {
|
|
22
|
+
const result = await (request.mode === 'reconcile' ? options.reconcile : options.execute)(request);
|
|
23
|
+
state = result.state;
|
|
24
|
+
const action = verification.actions.find(a => a.name === request.action.name && a.version === request.action.version);
|
|
25
|
+
if (state === 'succeeded')
|
|
26
|
+
validateSupportActionFields(result.result, action.output);
|
|
27
|
+
response = encodeSupportActionReceipt(request.request_id, state, result.result, result.error_code);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
state = 'unknown';
|
|
31
|
+
response = encodeSupportActionReceipt(request.request_id, state, {}, 'handler_outcome_unknown');
|
|
32
|
+
}
|
|
33
|
+
await options.store.complete('execution', key, claim.fence, response, state === 'unknown');
|
|
34
|
+
}
|
|
35
|
+
await options.store.complete('delivery', deliveryKey, deliveryClaim.fence, response);
|
|
36
|
+
return { body: response, headers: await signSupportAction(response, ACTION_RECEIPT_CONTEXT, verification.keyID, verification.secret, verification.now, delivery) };
|
|
37
|
+
}
|