zupost 0.1.1 → 0.5.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/index.d.ts CHANGED
@@ -1,8 +1,28 @@
1
1
  import { Options } from '@react-email/render';
2
2
  import { ReactNode } from 'react';
3
3
 
4
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ /**
6
+ * @internal
7
+ * Internal HTTP client for the Zupost API. Not part of the public API surface.
8
+ */
9
+ declare class HttpClient {
10
+ #private;
11
+ constructor(apiKey: string, baseUrl: string, userAgent: string);
12
+ request<T = unknown>(path: string, method: HttpMethod, body?: unknown): Promise<T>;
13
+ get<T = unknown>(path: string): Promise<T>;
14
+ post<T = unknown>(path: string, body: unknown): Promise<T>;
15
+ patch<T = unknown>(path: string, body: unknown): Promise<T>;
16
+ delete<T = unknown>(path: string, body?: unknown): Promise<T>;
17
+ }
18
+
4
19
  /**
5
20
  * Options for sending an email.
21
+ *
22
+ * Zupost is template first: the recommended way to send is by providing a
23
+ * `templateId` (managed in the Zupost dashboard) together with `variables`.
24
+ * `html`, `markdown`, and `react` are supported as fallbacks for ad-hoc or
25
+ * dynamically generated content. Exactly one content source is required.
6
26
  */
7
27
  interface SendEmailOptions {
8
28
  /**
@@ -33,23 +53,28 @@ interface SendEmailOptions {
33
53
  */
34
54
  bcc?: string | string[];
35
55
  /**
36
- * ID of the email template to use.
56
+ * ID of the email template to use. Preferred way of sending: content and
57
+ * subject are managed in the Zupost dashboard and can be changed without a
58
+ * deploy. Combine with `variables` for interpolation.
37
59
  */
38
60
  templateId?: string;
39
61
  /**
40
- * Variables for template interpolation.
62
+ * Variables for template interpolation. Used together with `templateId`.
41
63
  */
42
64
  variables?: Record<string, string>;
43
65
  /**
44
- * Markdown content for the email.
66
+ * Markdown content for the email. Fallback to `templateId` — use for ad-hoc
67
+ * or dynamically generated content.
45
68
  */
46
69
  markdown?: string;
47
70
  /**
48
- * HTML content for the email.
71
+ * HTML content for the email. Fallback to `templateId` — use for ad-hoc or
72
+ * dynamically generated content.
49
73
  */
50
74
  html?: string;
51
75
  /**
52
- * React component for the email.
76
+ * React component for the email. Fallback to `templateId` — use for ad-hoc
77
+ * or dynamically generated content rendered via React Email.
53
78
  */
54
79
  react?: ReactNode;
55
80
  /**
@@ -99,36 +124,40 @@ interface SendEmailResponse {
99
124
  * Emails API for sending transactional emails.
100
125
  */
101
126
  declare class Emails {
102
- private readonly zupost;
103
- constructor(zupost: Zupost);
127
+ private readonly http;
128
+ constructor(http: HttpClient);
104
129
  /**
105
- * Send an email using template, markdown or HTML content.
130
+ * Send an email.
131
+ *
132
+ * Zupost is template first: prefer sending by `templateId` with `variables`
133
+ * so your content lives in the Zupost dashboard and can be edited without a
134
+ * deploy. Inline `html`, `markdown`, or `react` content is supported as a
135
+ * fallback for ad-hoc or dynamically generated emails.
106
136
  *
107
137
  * @param options - Email options including recipient, sender, and content.
108
- * @returns A promise that resolves to the email response containing emailId.
138
+ * @returns A promise that resolves to the email response containing the email id.
109
139
  *
110
140
  * @example
111
141
  * ```typescript
112
142
  * const zupost = new Zupost('your-api-key');
113
143
  *
114
- * // Send with HTML content
115
- * const { emailId } = await zupost.emails.send({
144
+ * // Recommended: send with a template
145
+ * const { id } = await zupost.emails.send({
116
146
  * from: 'sender@example.com',
117
147
  * to: 'recipient@example.com',
118
- * subject: 'Hello World',
119
- * html: '<h1>Hello!</h1>',
148
+ * templateId: 'welcome-template',
149
+ * variables: { name: 'John' },
120
150
  * });
121
151
  *
122
- * // Send with a template
152
+ * // Fallback: inline HTML
123
153
  * await zupost.emails.send({
124
154
  * from: 'sender@example.com',
125
- * to: ['user1@example.com', 'user2@example.com'],
126
- * subject: 'Welcome!',
127
- * templateId: 'welcome-template',
128
- * variables: { name: 'John' },
155
+ * to: 'recipient@example.com',
156
+ * subject: 'Hello World',
157
+ * html: '<h1>Hello!</h1>',
129
158
  * });
130
159
  *
131
- * // Send with markdown
160
+ * // Fallback: inline markdown
132
161
  * await zupost.emails.send({
133
162
  * from: 'sender@example.com',
134
163
  * to: 'recipient@example.com',
@@ -168,8 +197,8 @@ interface SendCampaignResponse {
168
197
  * Campaigns API for sending email campaigns to audiences.
169
198
  */
170
199
  declare class Campaigns {
171
- private readonly zupost;
172
- constructor(zupost: Zupost);
200
+ private readonly http;
201
+ constructor(http: HttpClient);
173
202
  /**
174
203
  * Send a campaign to selected audiences.
175
204
  *
@@ -192,6 +221,403 @@ declare class Campaigns {
192
221
  send(options: SendCampaignOptions): Promise<SendCampaignResponse>;
193
222
  }
194
223
 
224
+ type ContactEventType = 'CONTACT_CREATED' | 'CONTACT_UPDATED' | 'EMAIL_SENT' | 'EMAIL_DELIVERED' | 'EMAIL_OPENED' | 'EMAIL_CLICKED' | 'EMAIL_BOUNCED' | 'EMAIL_COMPLAINED' | 'EMAIL_FAILED' | 'SUBSCRIBED' | 'UNSUBSCRIBED' | 'MARKETING_CONSENT_GIVEN' | 'MARKETING_CONSENT_WITHDRAWN' | 'TRACKING_CONSENT_GIVEN' | 'TRACKING_CONSENT_WITHDRAWN' | 'REPLY_RECEIVED' | 'SEQUENCE_ENTERED' | 'SEQUENCE_EXITED' | 'SEQUENCE_STEP_COMPLETED' | 'TAG_ADDED' | 'TAG_REMOVED' | 'FORGOTTEN';
225
+ type EmailRecipientType = 'TO' | 'CC' | 'BCC';
226
+ interface ContactTag {
227
+ name: string;
228
+ createdAt: string;
229
+ }
230
+ interface Contact {
231
+ id: string;
232
+ email: string;
233
+ name: string | null;
234
+ firstSeenAt: string;
235
+ firstSeenSource: string;
236
+ metadata?: unknown;
237
+ marketingConsent: boolean;
238
+ marketingConsentAt?: string | null;
239
+ marketingConsentSource?: string | null;
240
+ trackingConsent: boolean;
241
+ trackingConsentAt?: string | null;
242
+ birthday?: string | null;
243
+ createdAt: string;
244
+ updatedAt: string;
245
+ tags: ContactTag[];
246
+ }
247
+ interface ContactEvent {
248
+ id: string;
249
+ type: ContactEventType;
250
+ payload?: unknown;
251
+ emailId?: string | null;
252
+ sequenceRunId?: string | null;
253
+ source: string;
254
+ createdAt: string;
255
+ }
256
+ interface CreateContactOptions {
257
+ email: string;
258
+ name?: string;
259
+ metadata?: Record<string, unknown>;
260
+ marketingConsent?: boolean;
261
+ marketingConsentSource?: string;
262
+ trackingConsent?: boolean;
263
+ tags?: string[];
264
+ }
265
+ interface UpdateContactOptions {
266
+ name?: string | null;
267
+ metadata?: Record<string, unknown> | null;
268
+ birthday?: string | null;
269
+ marketingConsent?: boolean;
270
+ marketingConsentSource?: string | null;
271
+ trackingConsent?: boolean;
272
+ }
273
+ interface ListContactsOptions {
274
+ skip?: number;
275
+ take?: number;
276
+ search?: string;
277
+ marketingConsent?: boolean;
278
+ tag?: string;
279
+ }
280
+ interface ListContactsResponse {
281
+ items: Contact[];
282
+ total: number;
283
+ skip: number;
284
+ take: number;
285
+ }
286
+ interface ListEventsOptions {
287
+ skip?: number;
288
+ take?: number;
289
+ type?: ContactEventType | ContactEventType[];
290
+ }
291
+ interface ListEventsResponse {
292
+ items: ContactEvent[];
293
+ total: number;
294
+ skip: number;
295
+ take: number;
296
+ }
297
+ interface ContactExport {
298
+ contact: Contact;
299
+ subscriptions: Array<{
300
+ id: string;
301
+ status: string;
302
+ source: string;
303
+ createdAt: string;
304
+ unsubscribedAt: string | null;
305
+ audiences: Array<{
306
+ id: string;
307
+ name: string;
308
+ }>;
309
+ }>;
310
+ tags: ContactTag[];
311
+ emails: Array<{
312
+ emailId: string;
313
+ subject: string | null;
314
+ from: string;
315
+ usageType: 'TRANSACTIONAL' | 'MARKETING';
316
+ latestStatus: string;
317
+ recipientType: EmailRecipientType;
318
+ sentAt: string;
319
+ }>;
320
+ timeline: ContactEvent[];
321
+ exportedAt: string;
322
+ }
323
+ interface ForgetContactResponse {
324
+ id: string;
325
+ forgottenAt: string;
326
+ }
327
+
328
+ /**
329
+ * Contacts API.
330
+ *
331
+ * A Contact is every email address Zupost has ever seen, with a chronological
332
+ * event timeline, consent state (marketing and tracking), tags and GDPR
333
+ * primitives (data export, right to be forgotten).
334
+ *
335
+ * Marketing emails require `marketingConsent: true` and an audience membership
336
+ * (Subscriber). Transactional emails do not require consent and can be sent
337
+ * to any contact or even an ad-hoc address.
338
+ */
339
+ declare class Contacts {
340
+ private readonly http;
341
+ constructor(http: HttpClient);
342
+ /**
343
+ * Create or upsert a contact by email. Idempotent on (project, email).
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * const contact = await zupost.contacts.create({
348
+ * email: 'user@example.com',
349
+ * name: 'Jane Doe',
350
+ * marketingConsent: true,
351
+ * marketingConsentSource: 'signup-form',
352
+ * tags: ['vip', 'beta'],
353
+ * });
354
+ * ```
355
+ */
356
+ create(options: CreateContactOptions): Promise<Contact>;
357
+ /**
358
+ * Get a contact by ID.
359
+ */
360
+ get(id: string): Promise<Contact>;
361
+ /**
362
+ * List contacts with optional search, consent and tag filters.
363
+ */
364
+ list(options?: ListContactsOptions): Promise<ListContactsResponse>;
365
+ /**
366
+ * Update a contact. Pass only the fields that should change.
367
+ *
368
+ * Toggling `marketingConsent` or `trackingConsent` automatically records
369
+ * a corresponding ContactEvent for the audit trail.
370
+ */
371
+ update(id: string, patch: UpdateContactOptions): Promise<Contact>;
372
+ /**
373
+ * GDPR right-to-be-forgotten. Anonymises the contact, sets `forgottenAt`
374
+ * and disables consent. Existing email events are anonymised in a
375
+ * follow-up cascade.
376
+ */
377
+ forget(id: string): Promise<ForgetContactResponse>;
378
+ /**
379
+ * Get the chronological event timeline for a contact.
380
+ */
381
+ events(id: string, options?: ListEventsOptions): Promise<ListEventsResponse>;
382
+ /**
383
+ * GDPR data access export. Returns the complete record for a contact
384
+ * including all emails, events, subscriptions and tags.
385
+ */
386
+ export(id: string): Promise<ContactExport>;
387
+ /**
388
+ * Add a tag to a contact (idempotent).
389
+ */
390
+ addTag(id: string, name: string): Promise<ContactTag>;
391
+ /**
392
+ * Remove a tag from a contact.
393
+ */
394
+ removeTag(id: string, name: string): Promise<{
395
+ ok: true;
396
+ }>;
397
+ }
398
+
399
+ type SequenceType = 'TRANSACTIONAL' | 'MARKETING';
400
+ type SequenceStatus = 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED';
401
+ type SequenceTriggerType = 'MANUAL' | 'API' | 'AUDIENCE_SUBSCRIBE' | 'AUDIENCE_UNSUBSCRIBE' | 'TAG_ADDED' | 'TAG_REMOVED' | 'WEBHOOK' | 'SCHEDULED';
402
+ type SequenceRunStatus = 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
403
+ type WaitDuration = {
404
+ minutes?: number;
405
+ hours?: number;
406
+ days?: number;
407
+ };
408
+ type BranchCondition = {
409
+ kind: 'has_tag';
410
+ tag: string;
411
+ } | {
412
+ kind: 'opened_email';
413
+ sinceStepKey: string;
414
+ } | {
415
+ kind: 'clicked_email';
416
+ sinceStepKey: string;
417
+ } | {
418
+ kind: 'always';
419
+ };
420
+ type SequenceStep = {
421
+ key: string;
422
+ type: 'send_email';
423
+ name?: string;
424
+ config: {
425
+ templateId?: string;
426
+ subject?: string;
427
+ html?: string;
428
+ variables?: Record<string, unknown>;
429
+ fromHandle?: string;
430
+ fromDomainId?: string;
431
+ };
432
+ next?: string | null;
433
+ } | {
434
+ key: string;
435
+ type: 'wait';
436
+ name?: string;
437
+ config: {
438
+ duration: WaitDuration;
439
+ };
440
+ next?: string | null;
441
+ } | {
442
+ key: string;
443
+ type: 'branch';
444
+ name?: string;
445
+ config: {
446
+ condition: BranchCondition;
447
+ };
448
+ thenNext: string | null;
449
+ elseNext: string | null;
450
+ } | {
451
+ key: string;
452
+ type: 'tag_add' | 'tag_remove';
453
+ name?: string;
454
+ config: {
455
+ tag: string;
456
+ };
457
+ next?: string | null;
458
+ } | {
459
+ key: string;
460
+ type: 'update_contact';
461
+ name?: string;
462
+ config: {
463
+ fields: Record<string, unknown>;
464
+ };
465
+ next?: string | null;
466
+ } | {
467
+ key: string;
468
+ type: 'webhook';
469
+ name?: string;
470
+ config: {
471
+ url: string;
472
+ method?: 'GET' | 'POST';
473
+ headers?: Record<string, string>;
474
+ body?: unknown;
475
+ };
476
+ next?: string | null;
477
+ } | {
478
+ key: string;
479
+ type: 'end';
480
+ name?: string;
481
+ };
482
+ type SequenceDefinition = {
483
+ entry: string;
484
+ steps: Record<string, SequenceStep>;
485
+ };
486
+ interface SequenceVersion {
487
+ id: string;
488
+ version: number;
489
+ definition: SequenceDefinition;
490
+ createdBy?: string | null;
491
+ createdAt: string;
492
+ activatedAt?: string | null;
493
+ }
494
+ interface Sequence {
495
+ id: string;
496
+ name: string;
497
+ description?: string | null;
498
+ type: SequenceType;
499
+ status: SequenceStatus;
500
+ triggerType: SequenceTriggerType;
501
+ triggerConfig?: unknown;
502
+ activeVersionId?: string | null;
503
+ activeVersion?: SequenceVersion | null;
504
+ createdAt: string;
505
+ updatedAt: string;
506
+ }
507
+ interface SequenceRun {
508
+ id: string;
509
+ sequenceId: string;
510
+ sequenceVersionId: string;
511
+ contactId?: string | null;
512
+ adHocRecipient?: string | null;
513
+ payload?: unknown;
514
+ status: SequenceRunStatus;
515
+ currentStepKey?: string | null;
516
+ nextStepAt?: string | null;
517
+ enteredAt: string;
518
+ completedAt?: string | null;
519
+ error?: string | null;
520
+ }
521
+ interface CreateSequenceOptions {
522
+ name: string;
523
+ description?: string;
524
+ type?: SequenceType;
525
+ triggerType?: SequenceTriggerType;
526
+ triggerConfig?: unknown;
527
+ definition?: SequenceDefinition;
528
+ }
529
+ interface ListSequencesOptions {
530
+ skip?: number;
531
+ take?: number;
532
+ type?: SequenceType;
533
+ status?: SequenceStatus;
534
+ search?: string;
535
+ }
536
+ interface ListSequencesResponse {
537
+ items: Sequence[];
538
+ total: number;
539
+ skip: number;
540
+ take: number;
541
+ }
542
+ interface SaveVersionOptions {
543
+ definition: SequenceDefinition;
544
+ activate?: boolean;
545
+ }
546
+ interface TriggerRunOptions {
547
+ contactId?: string;
548
+ email?: string;
549
+ payload?: unknown;
550
+ }
551
+ interface ListRunsOptions {
552
+ skip?: number;
553
+ take?: number;
554
+ status?: SequenceRunStatus;
555
+ contactId?: string;
556
+ }
557
+ interface ListRunsResponse {
558
+ items: SequenceRun[];
559
+ total: number;
560
+ skip: number;
561
+ take: number;
562
+ }
563
+
564
+ /**
565
+ * Sequences API.
566
+ *
567
+ * A Sequence is a multi-step email journey. There are two kinds:
568
+ *
569
+ * - MARKETING: requires marketing consent on the recipient and an
570
+ * unsubscribe link in every send. Triggers off audience subscribe,
571
+ * tags, etc.
572
+ * - TRANSACTIONAL: no consent required, no unsubscribe link. Triggers
573
+ * off API events, webhooks or schedules.
574
+ *
575
+ * Define a sequence in code with the `defineSequence` helper, then
576
+ * `client.sequences.saveVersion` to push it.
577
+ */
578
+ declare class Sequences {
579
+ private readonly http;
580
+ constructor(http: HttpClient);
581
+ readonly runs: {
582
+ list: (sequenceId: string, options?: ListRunsOptions) => Promise<ListRunsResponse>;
583
+ };
584
+ /**
585
+ * Create a new sequence (always starts in DRAFT). Pass `definition` to
586
+ * seed the first version, or omit it to create an empty placeholder you
587
+ * can later push versions into.
588
+ */
589
+ create(options: CreateSequenceOptions): Promise<Sequence>;
590
+ /**
591
+ * Get a sequence by ID.
592
+ */
593
+ get(id: string): Promise<Sequence>;
594
+ /**
595
+ * List sequences with filters.
596
+ */
597
+ list(options?: ListSequencesOptions): Promise<ListSequencesResponse>;
598
+ /**
599
+ * Delete a sequence and all its versions and runs.
600
+ */
601
+ delete(id: string): Promise<{
602
+ id: string;
603
+ }>;
604
+ /**
605
+ * Change the lifecycle status: DRAFT, ACTIVE, PAUSED, ARCHIVED.
606
+ */
607
+ setStatus(id: string, status: SequenceStatus): Promise<Sequence>;
608
+ /**
609
+ * Save a new immutable version of the sequence definition. If `activate`
610
+ * is true (default), the new version becomes the active one for future
611
+ * triggered runs. Existing in-flight runs keep their pinned version.
612
+ */
613
+ saveVersion(id: string, options: SaveVersionOptions): Promise<SequenceVersion>;
614
+ /**
615
+ * Trigger a sequence run for a contact or ad-hoc email. The sequence
616
+ * must be ACTIVE.
617
+ */
618
+ trigger(id: string, options: TriggerRunOptions): Promise<SequenceRun>;
619
+ }
620
+
195
621
  interface ZupostOptions {
196
622
  /**
197
623
  * Custom API URL. Defaults to 'https://api.zupost.com'.
@@ -199,8 +625,7 @@ interface ZupostOptions {
199
625
  baseUrl?: string;
200
626
  }
201
627
  declare class Zupost {
202
- private readonly headers;
203
- private readonly baseUrl;
628
+ #private;
204
629
  /**
205
630
  * Emails API for sending transactional emails.
206
631
  */
@@ -209,22 +634,83 @@ declare class Zupost {
209
634
  * Campaigns API for sending email campaigns to audiences.
210
635
  */
211
636
  readonly campaigns: Campaigns;
212
- constructor(apiKey: string, options?: ZupostOptions);
213
- /**
214
- * @internal
215
- * Makes a request to the Zupost API.
216
- */
217
- request<T = unknown>(path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE', body?: unknown): Promise<T>;
218
637
  /**
219
- * @internal
220
- * Makes a GET request to the Zupost API.
638
+ * Contacts API. A Contact is every email address Zupost has ever seen,
639
+ * with a chronological event timeline, consent state and GDPR primitives.
221
640
  */
222
- get<T = unknown>(path: string): Promise<T>;
641
+ readonly contacts: Contacts;
223
642
  /**
224
- * @internal
225
- * Makes a POST request to the Zupost API.
643
+ * Sequences API. A Sequence is a multi-step email journey (marketing or
644
+ * transactional) with branches, waits, tags and webhooks.
226
645
  */
227
- post<T = unknown>(path: string, body: unknown): Promise<T>;
646
+ readonly sequences: Sequences;
647
+ constructor(apiKey: string, options?: ZupostOptions);
228
648
  }
229
649
 
230
- export { type SendCampaignOptions, type SendCampaignResponse, type SendEmailOptions, type SendEmailResponse, Zupost, type ZupostOptions };
650
+ /**
651
+ * Error thrown when the Zupost API returns a non-2xx response.
652
+ */
653
+ declare class ZupostError extends Error {
654
+ readonly status: number;
655
+ readonly statusText: string;
656
+ readonly body: string;
657
+ readonly requestId?: string;
658
+ readonly code?: string;
659
+ constructor(args: {
660
+ message: string;
661
+ status: number;
662
+ statusText: string;
663
+ body: string;
664
+ requestId?: string;
665
+ code?: string;
666
+ });
667
+ /** @internal */
668
+ static fromResponse(response: Response): Promise<ZupostError>;
669
+ }
670
+
671
+ /**
672
+ * Type-safe helper for defining a SequenceDefinition in code.
673
+ *
674
+ * Catches missing entry steps and unreachable next-pointers at compile time
675
+ * (via the explicit literal types) and at runtime (via the assertion below).
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * const onboarding = defineSequence({
680
+ * entry: 'welcome',
681
+ * steps: {
682
+ * welcome: {
683
+ * key: 'welcome',
684
+ * type: 'send_email',
685
+ * config: { templateId: 'welcome-mail' },
686
+ * next: 'wait_3d',
687
+ * },
688
+ * wait_3d: {
689
+ * key: 'wait_3d',
690
+ * type: 'wait',
691
+ * config: { duration: { days: 3 } },
692
+ * next: 'tips',
693
+ * },
694
+ * tips: {
695
+ * key: 'tips',
696
+ * type: 'send_email',
697
+ * config: { templateId: 'tips-mail' },
698
+ * next: 'end',
699
+ * },
700
+ * end: { key: 'end', type: 'end' },
701
+ * },
702
+ * });
703
+ *
704
+ * await zupost.sequences.create({
705
+ * name: 'Onboarding',
706
+ * type: 'MARKETING',
707
+ * definition: onboarding,
708
+ * });
709
+ * ```
710
+ */
711
+ declare function defineSequence<TSteps extends Record<string, SequenceStep>>(definition: {
712
+ entry: keyof TSteps & string;
713
+ steps: TSteps;
714
+ }): SequenceDefinition;
715
+
716
+ export { type BranchCondition, type Contact, type ContactEvent, type ContactEventType, type ContactExport, type ContactTag, type CreateContactOptions, type CreateSequenceOptions, type EmailRecipientType, type ForgetContactResponse, type ListContactsOptions, type ListContactsResponse, type ListEventsOptions, type ListEventsResponse, type ListRunsOptions, type ListRunsResponse, type ListSequencesOptions, type ListSequencesResponse, type SaveVersionOptions, type SendCampaignOptions, type SendCampaignResponse, type SendEmailOptions, type SendEmailResponse, type Sequence, type SequenceDefinition, type SequenceRun, type SequenceRunStatus, type SequenceStatus, type SequenceStep, type SequenceTriggerType, type SequenceType, type SequenceVersion, type TriggerRunOptions, type UpdateContactOptions, type WaitDuration, Zupost, ZupostError, type ZupostOptions, defineSequence };