@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.
Files changed (39) hide show
  1. package/README.md +68 -0
  2. package/dist/asset-media-v2.d.ts +15 -1
  3. package/dist/asset-workflows.d.ts +5 -13
  4. package/dist/assets-dam-types.d.ts +35 -0
  5. package/dist/assets.d.ts +11 -2
  6. package/dist/assets.js +22 -0
  7. package/dist/client.d.ts +2 -0
  8. package/dist/client.js +5 -1
  9. package/dist/generated/assets-contract.d.ts +41 -6
  10. package/dist/generated/assets-contract.js +8 -1
  11. package/dist/index.d.ts +12 -3
  12. package/dist/index.js +7 -0
  13. package/dist/mail-billing.d.ts +12 -0
  14. package/dist/mail-billing.js +3 -0
  15. package/dist/mail-brands.d.ts +14 -1
  16. package/dist/mail-campaigns.d.ts +6 -3
  17. package/dist/mail-campaigns.js +6 -4
  18. package/dist/mail-delivery-types.d.ts +1 -0
  19. package/dist/mail-events-webhooks.d.ts +8 -3
  20. package/dist/mail-events-webhooks.js +13 -0
  21. package/dist/mail-mailboxes.d.ts +358 -0
  22. package/dist/mail-mailboxes.js +202 -0
  23. package/dist/mail-webhook-types.d.ts +40 -1
  24. package/dist/mail.d.ts +8 -0
  25. package/dist/mail.js +15 -0
  26. package/dist/partner.d.ts +369 -0
  27. package/dist/partner.js +70 -0
  28. package/dist/support-action-manifest.d.ts +3 -0
  29. package/dist/support-action-manifest.js +40 -0
  30. package/dist/support-action-receiver.d.ts +20 -0
  31. package/dist/support-action-receiver.js +37 -0
  32. package/dist/support-action-sqlite.d.ts +17 -0
  33. package/dist/support-action-sqlite.js +30 -0
  34. package/dist/support-action-types.d.ts +93 -0
  35. package/dist/support-action-types.js +1 -0
  36. package/dist/support-actions.d.ts +19 -0
  37. package/dist/support-actions.js +213 -0
  38. package/dist/types.d.ts +5 -0
  39. 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
+ }
@@ -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
+ }