@sendmailos/sdk 1.2.0 → 1.2.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.js CHANGED
@@ -58,9 +58,10 @@ var EmailsResource = class {
58
58
  }
59
59
  /**
60
60
  * Send a transactional email
61
- *
61
+ *
62
62
  * @example
63
63
  * ```ts
64
+ * // Business account (single-tenant)
64
65
  * const result = await client.emails.send({
65
66
  * to: 'user@example.com',
66
67
  * fromName: 'Your Company',
@@ -68,6 +69,16 @@ var EmailsResource = class {
68
69
  * subject: 'Welcome!',
69
70
  * html: '<h1>Hello!</h1>'
70
71
  * });
72
+ *
73
+ * // Agency account (multi-tenant) - identify client by domain
74
+ * const result = await agencyClient.emails.send({
75
+ * to: 'customer@example.com',
76
+ * fromName: 'Pizza Palace',
77
+ * fromEmail: 'hello@pizzapalace.com',
78
+ * subject: 'Your order is ready!',
79
+ * html: '<h1>Order Ready!</h1>',
80
+ * domain: 'pizzapalace.com' // Required for agency accounts
81
+ * });
71
82
  * ```
72
83
  */
73
84
  async send(params) {
@@ -81,13 +92,15 @@ var EmailsResource = class {
81
92
  from_name: params.fromName,
82
93
  from_email: params.fromEmail,
83
94
  variables: params.variables,
84
- type: params.type || "transactional"
95
+ type: params.type || "transactional",
96
+ workspace_id: params.workspaceId,
97
+ domain: params.domain
85
98
  })
86
99
  });
87
100
  }
88
101
  /**
89
102
  * Send email using a template
90
- *
103
+ *
91
104
  * @example
92
105
  * ```ts
93
106
  * const result = await client.emails.sendTemplate({
@@ -105,7 +118,9 @@ var EmailsResource = class {
105
118
  fromName: params.fromName,
106
119
  fromEmail: params.fromEmail,
107
120
  variables: params.variables,
108
- type: "transactional"
121
+ type: "transactional",
122
+ workspaceId: params.workspaceId,
123
+ domain: params.domain
109
124
  });
110
125
  }
111
126
  };
@@ -117,14 +132,23 @@ var SubscribersResource = class {
117
132
  }
118
133
  /**
119
134
  * Create or update a subscriber (upsert)
120
- *
135
+ *
121
136
  * @example
122
137
  * ```ts
138
+ * // Business account
123
139
  * const { subscriber } = await client.subscribers.create({
124
140
  * email: 'user@example.com',
125
141
  * firstName: 'John',
126
142
  * tags: ['newsletter', 'premium']
127
143
  * });
144
+ *
145
+ * // Agency account - subscriber tracked to client domain
146
+ * const { subscriber } = await agencyClient.subscribers.create({
147
+ * email: 'customer@example.com',
148
+ * firstName: 'Jane',
149
+ * domain: 'pizzapalace.com', // Required for agency accounts
150
+ * tags: ['loyalty-member']
151
+ * });
128
152
  * ```
129
153
  */
130
154
  async create(params) {
@@ -135,7 +159,10 @@ var SubscribersResource = class {
135
159
  first_name: params.firstName,
136
160
  last_name: params.lastName,
137
161
  tags: params.tags,
138
- domain_id: params.domainId
162
+ domain_id: params.domainId,
163
+ workspace_id: params.workspaceId,
164
+ domain: params.domain
165
+ // Alternative to workspace_id
139
166
  })
140
167
  });
141
168
  }
@@ -154,6 +181,8 @@ var SubscribersResource = class {
154
181
  const searchParams = new URLSearchParams();
155
182
  if (params.limit) searchParams.set("limit", String(params.limit));
156
183
  if (params.offset) searchParams.set("offset", String(params.offset));
184
+ if (params.workspaceId) searchParams.set("workspace_id", params.workspaceId);
185
+ if (params.domain) searchParams.set("domain", params.domain);
157
186
  const query = searchParams.toString();
158
187
  const endpoint = query ? `/subscribers?${query}` : "/subscribers";
159
188
  return this.request(endpoint, {
@@ -225,9 +254,10 @@ var CampaignsResource = class {
225
254
  }
226
255
  /**
227
256
  * Send a campaign to subscribers
228
- *
257
+ *
229
258
  * @example
230
259
  * ```ts
260
+ * // Business account
231
261
  * const result = await client.campaigns.send({
232
262
  * name: 'January Newsletter',
233
263
  * subject: 'What\'s new this month',
@@ -236,6 +266,17 @@ var CampaignsResource = class {
236
266
  * html: '<h1>Hello {{first_name}}!</h1>',
237
267
  * tags: ['newsletter', 'active']
238
268
  * });
269
+ *
270
+ * // Agency account - send to specific client's subscribers
271
+ * const result = await agencyClient.campaigns.send({
272
+ * name: 'Pizza Promo',
273
+ * subject: 'New menu items!',
274
+ * fromName: 'Pizza Palace',
275
+ * fromEmail: 'promo@pizzapalace.com',
276
+ * html: '<h1>Check out our new menu!</h1>',
277
+ * domain: 'pizzapalace.com', // Filter to this client's subscribers
278
+ * sourceDomains: ['pizzapalace.com'] // Only subscribers who signed up on this domain
279
+ * });
239
280
  * ```
240
281
  */
241
282
  async send(params) {
@@ -249,7 +290,10 @@ var CampaignsResource = class {
249
290
  html: params.html,
250
291
  templateId: params.templateId,
251
292
  tags: params.tags,
252
- variables: params.variables
293
+ variables: params.variables,
294
+ workspace_id: params.workspaceId,
295
+ domain: params.domain,
296
+ sourceDomains: params.sourceDomains
253
297
  })
254
298
  });
255
299
  }
@@ -264,7 +308,10 @@ var CampaignsResource = class {
264
308
  fromEmail: params.fromEmail,
265
309
  templateId: params.templateId,
266
310
  tags: params.tags,
267
- variables: params.variables
311
+ variables: params.variables,
312
+ workspaceId: params.workspaceId,
313
+ domain: params.domain,
314
+ sourceDomains: params.sourceDomains
268
315
  });
269
316
  }
270
317
  };
@@ -556,6 +603,400 @@ var WorkspacesResource = class {
556
603
  client_can_view_reports: true
557
604
  });
558
605
  }
606
+ /**
607
+ * Smart workspace provisioning - finds existing or creates new
608
+ *
609
+ * This is the recommended method for agency client onboarding.
610
+ * It will:
611
+ * 1. Search for existing workspace by website URL or client_email
612
+ * 2. Return existing workspace if found
613
+ * 3. Create new workspace with industry templates if not found
614
+ * 4. Optionally generate a workspace-scoped API key
615
+ *
616
+ * @note Only available for agency accounts
617
+ *
618
+ * @example
619
+ * ```ts
620
+ * // Auto-provision workspace for a new client
621
+ * const result = await client.workspaces.provision({
622
+ * name: 'Pizza Palace',
623
+ * website: 'https://pizzapalace.com',
624
+ * client_email: 'owner@pizzapalace.com',
625
+ * industry: 'restaurants',
626
+ * generate_api_key: true
627
+ * });
628
+ *
629
+ * if (result.is_new) {
630
+ * console.log('Created new workspace:', result.workspace.id);
631
+ * console.log('API Key:', result.api_key);
632
+ * } else {
633
+ * console.log('Found existing workspace:', result.workspace.id);
634
+ * }
635
+ * ```
636
+ *
637
+ * @example
638
+ * ```ts
639
+ * // Idempotent client setup - safe to call multiple times
640
+ * const { workspace, is_new } = await client.workspaces.provision({
641
+ * name: 'Client Name',
642
+ * website: 'https://client.com',
643
+ * industry: 'ecommerce'
644
+ * });
645
+ * // Always returns the same workspace for the same website
646
+ * ```
647
+ */
648
+ async provision(request) {
649
+ const response = await this.request("/workspaces/provision", {
650
+ method: "POST",
651
+ body: JSON.stringify(request)
652
+ });
653
+ return {
654
+ workspace: response.workspace,
655
+ is_new: response.is_new,
656
+ api_key: response.api_key,
657
+ message: response.message
658
+ };
659
+ }
660
+ /**
661
+ * Alias for provision() - find or create workspace
662
+ * @see provision
663
+ */
664
+ async findOrCreate(request) {
665
+ return this.provision(request);
666
+ }
667
+ /**
668
+ * Complete client onboarding in ONE call (Agency only)
669
+ *
670
+ * This is the simplest way to onboard a new client:
671
+ * 1. Creates or finds workspace by domain
672
+ * 2. Registers domain for email verification
673
+ * 3. Generates workspace token
674
+ * 4. Returns DNS records + token
675
+ *
676
+ * After this, use the returned `token` for all API calls.
677
+ *
678
+ * @example
679
+ * ```ts
680
+ * // Your SaaS onboards a new restaurant
681
+ * const result = await agency.workspaces.onboard({
682
+ * domain: 'pizzapalace.com',
683
+ * industry: 'restaurants'
684
+ * });
685
+ *
686
+ * // Save this token - it's all you need
687
+ * saveToDatabase(restaurantId, result.token);
688
+ *
689
+ * // Show DNS records to restaurant owner
690
+ * console.log('Add these DNS records:', result.dns_records);
691
+ *
692
+ * // Later, use the token directly
693
+ * const restaurant = new SendMailOS(result.token);
694
+ * await restaurant.emails.send({ ... });
695
+ * ```
696
+ */
697
+ async onboard(request) {
698
+ const response = await this.request("/workspaces/onboard", {
699
+ method: "POST",
700
+ body: JSON.stringify(request)
701
+ });
702
+ return {
703
+ token: response.token,
704
+ workspace_id: response.workspace_id,
705
+ workspace_name: response.workspace_name,
706
+ domain: response.domain,
707
+ domain_status: response.domain_status,
708
+ dns_records: response.dns_records,
709
+ is_new: response.is_new,
710
+ message: response.message
711
+ };
712
+ }
713
+ };
714
+
715
+ // src/resources/workflows.ts
716
+ var WorkflowsResource = class {
717
+ constructor(request) {
718
+ this.request = request;
719
+ }
720
+ /**
721
+ * Create a new workflow
722
+ *
723
+ * @example
724
+ * ```ts
725
+ * // Create workflow for a specific client (multi-brand)
726
+ * const result = await platform.workflows.create({
727
+ * name: 'Welcome Sequence',
728
+ * domain: 'pizzapalace.com',
729
+ * trigger: { type: 'subscriber_created' }
730
+ * });
731
+ *
732
+ * // Create workflow (single brand)
733
+ * const result = await client.workflows.create({
734
+ * name: 'Onboarding Flow',
735
+ * trigger: { type: 'api' }
736
+ * });
737
+ * ```
738
+ */
739
+ async create(params) {
740
+ return this.request("/workflows", {
741
+ method: "POST",
742
+ body: JSON.stringify(params)
743
+ });
744
+ }
745
+ /**
746
+ * List all workflows
747
+ *
748
+ * @example
749
+ * ```ts
750
+ * // List all workflows
751
+ * const { workflows } = await client.workflows.list();
752
+ *
753
+ * // List workflows for a specific client (multi-brand)
754
+ * const { workflows } = await platform.workflows.list({ domain: 'pizzapalace.com' });
755
+ * ```
756
+ */
757
+ async list(params = {}) {
758
+ const searchParams = new URLSearchParams();
759
+ if (params.domain) searchParams.set("domain", params.domain);
760
+ if (params.workspace_id) searchParams.set("workspace_id", params.workspace_id);
761
+ const query = searchParams.toString();
762
+ const endpoint = query ? `/workflows?${query}` : "/workflows";
763
+ return this.request(endpoint, {
764
+ method: "GET"
765
+ });
766
+ }
767
+ /**
768
+ * Get a workflow by ID (includes nodes and edges)
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * const { workflow } = await client.workflows.get('workflow-uuid');
773
+ * console.log(workflow.nodes); // The workflow steps
774
+ * console.log(workflow.edges); // Connections between steps
775
+ * ```
776
+ */
777
+ async get(workflowId) {
778
+ return this.request(`/workflows/${workflowId}`, {
779
+ method: "GET"
780
+ });
781
+ }
782
+ /**
783
+ * Update a workflow (name, nodes, edges, trigger)
784
+ *
785
+ * @example
786
+ * ```ts
787
+ * // Update workflow name
788
+ * await client.workflows.update('workflow-uuid', { name: 'New Name' });
789
+ *
790
+ * // Update workflow steps
791
+ * await client.workflows.update('workflow-uuid', {
792
+ * nodes: [
793
+ * { id: '1', type: 'trigger', data: { triggerType: 'api' } },
794
+ * { id: '2', type: 'email', data: { templateId: 'tmpl_xxx', subject: 'Welcome!' } },
795
+ * { id: '3', type: 'delay', data: { duration: 3, unit: 'days' } }
796
+ * ],
797
+ * edges: [
798
+ * { id: 'e1', source: '1', target: '2' },
799
+ * { id: 'e2', source: '2', target: '3' }
800
+ * ]
801
+ * });
802
+ * ```
803
+ */
804
+ async update(workflowId, params) {
805
+ return this.request(`/workflows/${workflowId}`, {
806
+ method: "PUT",
807
+ body: JSON.stringify(params)
808
+ });
809
+ }
810
+ /**
811
+ * Delete a workflow
812
+ */
813
+ async delete(workflowId) {
814
+ return this.request(`/workflows/${workflowId}`, {
815
+ method: "DELETE"
816
+ });
817
+ }
818
+ /**
819
+ * Activate a workflow (start accepting triggers)
820
+ *
821
+ * @example
822
+ * ```ts
823
+ * await client.workflows.activate('workflow-uuid');
824
+ * // Workflow is now active and will process triggers
825
+ * ```
826
+ */
827
+ async activate(workflowId) {
828
+ return this.request(`/workflows/${workflowId}/activate`, {
829
+ method: "POST"
830
+ });
831
+ }
832
+ /**
833
+ * Pause a workflow (stop accepting new triggers)
834
+ *
835
+ * @example
836
+ * ```ts
837
+ * await client.workflows.pause('workflow-uuid');
838
+ * // Workflow is paused - existing executions continue, but no new ones start
839
+ * ```
840
+ */
841
+ async pause(workflowId) {
842
+ return this.request(`/workflows/${workflowId}/pause`, {
843
+ method: "POST"
844
+ });
845
+ }
846
+ /**
847
+ * Trigger a workflow for a subscriber
848
+ *
849
+ * @example
850
+ * ```ts
851
+ * // Trigger when a customer signs up
852
+ * const result = await platform.workflows.trigger('workflow-uuid', {
853
+ * email: 'customer@example.com',
854
+ * data: {
855
+ * firstName: 'John',
856
+ * restaurantName: 'Pizza Palace',
857
+ * signupSource: 'website'
858
+ * }
859
+ * });
860
+ *
861
+ * console.log(result.execution_id); // Track this execution
862
+ * ```
863
+ */
864
+ async trigger(workflowId, params) {
865
+ return this.request(`/webhooks/${workflowId}`, {
866
+ method: "POST",
867
+ body: JSON.stringify(params)
868
+ });
869
+ }
870
+ /**
871
+ * Send a custom event to resume waiting workflows
872
+ *
873
+ * Use this when you have workflows waiting for specific events (e.g., "purchase_completed").
874
+ * All workflows waiting for this event for the given subscriber will resume.
875
+ *
876
+ * @example
877
+ * ```ts
878
+ * // When a customer completes a purchase
879
+ * await platform.workflows.sendEvent({
880
+ * event: 'purchase_completed',
881
+ * email: 'customer@example.com',
882
+ * data: { orderTotal: 45.99, orderId: 'ORD-123' }
883
+ * });
884
+ *
885
+ * // Any workflow with a "wait_for_event: purchase_completed" node will resume
886
+ * ```
887
+ */
888
+ async sendEvent(params) {
889
+ return this.request("/events", {
890
+ method: "POST",
891
+ body: JSON.stringify(params)
892
+ });
893
+ }
894
+ };
895
+
896
+ // src/resources/webhooks.ts
897
+ var WebhooksResource = class {
898
+ constructor(request) {
899
+ this.request = request;
900
+ }
901
+ /**
902
+ * Create a new webhook endpoint
903
+ *
904
+ * @example
905
+ * ```ts
906
+ * const webhook = await client.webhooks.create({
907
+ * url: 'https://yourserver.com/webhooks',
908
+ * events: ['email.sent', 'email.delivered', 'email.opened'],
909
+ * secret: 'your-secret-key' // Optional, auto-generated if not provided
910
+ * });
911
+ *
912
+ * console.log('Webhook secret:', webhook.secret);
913
+ * ```
914
+ */
915
+ async create(params) {
916
+ return this.request("/webhooks", {
917
+ method: "POST",
918
+ body: JSON.stringify(params)
919
+ });
920
+ }
921
+ /**
922
+ * List all webhooks
923
+ */
924
+ async list() {
925
+ return this.request("/webhooks", {
926
+ method: "GET"
927
+ });
928
+ }
929
+ /**
930
+ * Get a webhook by ID with recent delivery history
931
+ */
932
+ async get(webhookId) {
933
+ return this.request(`/webhooks/${webhookId}`, {
934
+ method: "GET"
935
+ });
936
+ }
937
+ /**
938
+ * Update a webhook
939
+ *
940
+ * @example
941
+ * ```ts
942
+ * // Update webhook URL
943
+ * await client.webhooks.update('webhook-id', {
944
+ * url: 'https://newserver.com/webhooks'
945
+ * });
946
+ *
947
+ * // Add new events
948
+ * await client.webhooks.update('webhook-id', {
949
+ * events: ['email.sent', 'email.delivered', 'email.bounced']
950
+ * });
951
+ *
952
+ * // Disable webhook
953
+ * await client.webhooks.update('webhook-id', { active: false });
954
+ * ```
955
+ */
956
+ async update(webhookId, params) {
957
+ return this.request(`/webhooks/${webhookId}`, {
958
+ method: "PATCH",
959
+ body: JSON.stringify(params)
960
+ });
961
+ }
962
+ /**
963
+ * Delete a webhook
964
+ */
965
+ async delete(webhookId) {
966
+ return this.request(`/webhooks/${webhookId}`, {
967
+ method: "DELETE"
968
+ });
969
+ }
970
+ /**
971
+ * Test a webhook by sending a test event
972
+ *
973
+ * @example
974
+ * ```ts
975
+ * const result = await client.webhooks.test('webhook-id');
976
+ * if (result.test.sent) {
977
+ * console.log(`Test delivered in ${result.test.responseTimeMs}ms`);
978
+ * } else {
979
+ * console.log('Test failed:', result.test.error);
980
+ * }
981
+ * ```
982
+ */
983
+ async test(webhookId) {
984
+ return this.request(`/webhooks/${webhookId}/test`, {
985
+ method: "POST"
986
+ });
987
+ }
988
+ /**
989
+ * Enable a webhook
990
+ */
991
+ async enable(webhookId) {
992
+ return this.update(webhookId, { active: true });
993
+ }
994
+ /**
995
+ * Disable a webhook
996
+ */
997
+ async disable(webhookId) {
998
+ return this.update(webhookId, { active: false });
999
+ }
559
1000
  };
560
1001
 
561
1002
  // src/client.ts
@@ -588,6 +1029,8 @@ var SendMailOS = class {
588
1029
  this.domains = new DomainsResource(boundRequest);
589
1030
  this.organization = new OrganizationResource(boundRequest);
590
1031
  this.workspaces = new WorkspacesResource(boundRequest);
1032
+ this.workflows = new WorkflowsResource(boundRequest);
1033
+ this.webhooks = new WebhooksResource(boundRequest);
591
1034
  }
592
1035
  /**
593
1036
  * Make an authenticated request to the API