loops_sdk 2.1.0 → 2.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65b81eba842a1765bb2bc99006cbcaab839e442b3ee90ea92076cef8fba8f942
4
- data.tar.gz: 3e544adfb4c2ed0b1069fc1a455773397a27a8254d9e8fe75ebbc33be2dfa9e8
3
+ metadata.gz: 5a4fac371d3bbc89f778265ecb287032e0ce08846a688b61887a64f27e30f8f4
4
+ data.tar.gz: 4c0647edf1dcebf7927232332ad5d92d8114c5ce8b291b1cbffc0c4dd145455e
5
5
  SHA512:
6
- metadata.gz: 565a16854a58cf5510b8c6c189394f7b62bea1aa6b1448e41dbb1759268d6f25fe9f366190a29896b974a49648e2068478db8e4622c9db3bbb2a29ef25aba855
7
- data.tar.gz: cd2b0f20ad57991723c3ca2bb68efe71b3759a0669a602be1c71583368fb7f3ae79dabbbb068ba9392918081edf9e465fcd4ee1f50c721ea5cbbcd2cc866d010
6
+ metadata.gz: 592a434bc0dd7d9c19c2b209eacd2463373cbeca1ed6d67e8fb376443084ae1e3c24ef95b2ee85d225e6c4ee544badd16fe34b6b80c262192817c77d501cf8a3
7
+ data.tar.gz: 2e979c1b6ddd315fd3adc6d28fedd69bf19322207ca66bf85eef4ea799d4f94dce01ffb55205fe23dcd7da872e132e976a83f11fcb6d0755744388b990a528a9
data/README.md CHANGED
@@ -114,8 +114,39 @@ You can use custom contact properties in API calls. Please make sure to [add cus
114
114
  - [ContactProperties.list()](#contactpropertieslist)
115
115
  - [MailingLists.list()](#mailinglistslist)
116
116
  - [Events.send()](#eventssend)
117
- - [Transactional.send()](#transactionalsend)
118
117
  - [Transactional.list()](#transactionallist)
118
+ - [Transactional.create()](#transactionalcreate)
119
+ - [Transactional.get()](#transactionalget)
120
+ - [Transactional.update()](#transactionalupdate)
121
+ - [Transactional.ensure_draft()](#transactionalensure_draft)
122
+ - [Transactional.publish()](#transactionalpublish)
123
+ - [Transactional.send()](#transactionalsend)
124
+ - [DedicatedSendingIps.list()](#dedicatedsendingipslist)
125
+ - [Themes.list()](#themeslist)
126
+ - [Themes.get()](#themesget)
127
+ - [Components.list()](#componentslist)
128
+ - [Components.get()](#componentsget)
129
+ - [Campaigns.list()](#campaignslist)
130
+ - [Campaigns.create()](#campaignscreate)
131
+ - [Campaigns.get()](#campaignsget)
132
+ - [Campaigns.update()](#campaignsupdate)
133
+ - [CampaignGroups.list()](#campaigngroupslist)
134
+ - [CampaignGroups.create()](#campaigngroupscreate)
135
+ - [CampaignGroups.get()](#campaigngroupsget)
136
+ - [CampaignGroups.update()](#campaigngroupsupdate)
137
+ - [AudienceSegments.list()](#audiencesegmentslist)
138
+ - [AudienceSegments.get()](#audiencesegmentsget)
139
+ - [Workflows.list()](#workflowslist)
140
+ - [Workflows.get()](#workflowsget)
141
+ - [Workflows.get_node()](#workflowsget_node)
142
+ - [EmailMessages.get()](#emailmessagesget)
143
+ - [EmailMessages.update()](#emailmessagesupdate)
144
+ - [EmailMessages.preview()](#emailmessagespreview)
145
+ - [TransactionalGroups.list()](#transactionalgroupslist)
146
+ - [TransactionalGroups.create()](#transactionalgroupscreate)
147
+ - [TransactionalGroups.get()](#transactionalgroupsget)
148
+ - [TransactionalGroups.update()](#transactionalgroupsupdate)
149
+ - [Uploads.upload()](#uploadsupload)
119
150
 
120
151
  ---
121
152
 
@@ -195,7 +226,7 @@ This method will return a success or error message:
195
226
  ```json
196
227
  {
197
228
  "success": true,
198
- "id": "id_of_contact"
229
+ "id": "cll6b3i8901a9jx0oyktl2m4u"
199
230
  }
200
231
  ```
201
232
 
@@ -259,7 +290,7 @@ This method will return a success or error message:
259
290
  ```json
260
291
  {
261
292
  "success": true,
262
- "id": "id_of_contact"
293
+ "id": "cll6b3i8901a9jx0oyktl2m4u"
263
294
  }
264
295
  ```
265
296
 
@@ -715,6 +746,180 @@ This method will return a success or error:
715
746
 
716
747
  ---
717
748
 
749
+ ### Transactional.list()
750
+
751
+ List transactional emails, most recently created first.
752
+
753
+ [API Reference](https://loops.so/docs/api-reference/list-transactional-emails)
754
+
755
+ #### Parameters
756
+
757
+ | Name | Type | Required | Notes |
758
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
759
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
760
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
761
+
762
+ #### Example
763
+
764
+ ```ruby
765
+ response = LoopsSdk::Transactional.list
766
+
767
+ response = LoopsSdk::Transactional.list(perPage: 15, cursor: "cursor_value")
768
+ ```
769
+
770
+ #### Response
771
+
772
+ ```json
773
+ {
774
+ "pagination": {
775
+ "totalResults": 23,
776
+ "returnedResults": 20,
777
+ "perPage": 20,
778
+ "totalPages": 2,
779
+ "nextCursor": "clyo0q4wo01p59fsecyxqsh38",
780
+ "nextPage": "https://app.loops.so/api/v1/transactional-emails?cursor=clyo0q4wo01p59fsecyxqsh38&perPage=20"
781
+ },
782
+ "data": [
783
+ {
784
+ "id": "clfn0k1yg001imo0fdeqg30i8",
785
+ "name": "Welcome email",
786
+ "draftEmailMessageId": null,
787
+ "publishedEmailMessageId": "cly8k3m0n0044jpx2bghepq45",
788
+ "createdAt": "2023-11-06T17:48:07.249Z",
789
+ "updatedAt": "2023-11-06T17:48:07.249Z",
790
+ "dataVariables": ["confirmationUrl"]
791
+ }
792
+ ]
793
+ }
794
+ ```
795
+
796
+ ---
797
+
798
+ ### Transactional.create()
799
+
800
+ Create a transactional email. An empty draft email message is created automatically.
801
+
802
+ [API Reference](https://loops.so/docs/api-reference/create-transactional-email)
803
+
804
+ #### Parameters
805
+
806
+ | Name | Type | Required | Notes |
807
+ | ------------------------ | ------ | -------- | ------------------------------------------------------- |
808
+ | `name` | string | Yes | |
809
+ | `transactional_group_id` | string | No | The ID of the group to add this transactional email to. |
810
+
811
+ #### Example
812
+
813
+ ```ruby
814
+ response = LoopsSdk::Transactional.create(name: "Welcome email")
815
+ ```
816
+
817
+ #### Response
818
+
819
+ Returns the transactional email with `draftEmailMessageId` and `draftEmailMessageContentRevisionId`. Use these when updating the draft via `EmailMessages.update()`.
820
+
821
+ ```json
822
+ {
823
+ "id": "clfq6dinn000yl70fgwwyp82l",
824
+ "name": "Welcome email",
825
+ "draftEmailMessageId": "cly8k3m0n0044jpx2bghepq45",
826
+ "draftEmailMessageContentRevisionId": "clm9n4o6p0088lrz4dijslt67",
827
+ "publishedEmailMessageId": null,
828
+ "createdAt": "2023-11-06T17:48:07.249Z",
829
+ "updatedAt": "2023-11-06T17:48:07.249Z",
830
+ "dataVariables": []
831
+ }
832
+ ```
833
+
834
+ ---
835
+
836
+ ### Transactional.get()
837
+
838
+ Get a single transactional email by ID.
839
+
840
+ [API Reference](https://loops.so/docs/api-reference/get-transactional-email)
841
+
842
+ #### Parameters
843
+
844
+ | Name | Type | Required | Notes |
845
+ | ------------------ | ------ | -------- | ----- |
846
+ | `transactional_id` | string | Yes | |
847
+
848
+ #### Example
849
+
850
+ ```ruby
851
+ response = LoopsSdk::Transactional.get(transactional_id: "clfq6dinn000yl70fgwwyp82l")
852
+ ```
853
+
854
+ ---
855
+
856
+ ### Transactional.update()
857
+
858
+ Update a transactional email's name.
859
+
860
+ [API Reference](https://loops.so/docs/api-reference/update-transactional-email)
861
+
862
+ #### Parameters
863
+
864
+ | Name | Type | Required | Notes |
865
+ | ------------------------ | ------ | -------- | ------------------------------------------------------------ |
866
+ | `transactional_id` | string | Yes | |
867
+ | `name` | string | No | |
868
+ | `transactional_group_id` | string | No | The ID of the group to move this transactional email to. |
869
+
870
+ At least one field must be provided.
871
+
872
+ #### Example
873
+
874
+ ```ruby
875
+ response = LoopsSdk::Transactional.update(
876
+ transactional_id: "clfq6dinn000yl70fgwwyp82l",
877
+ name: "Updated name"
878
+ )
879
+ ```
880
+
881
+ ---
882
+
883
+ ### Transactional.ensure_draft()
884
+
885
+ Ensure a transactional email has a draft email message. If a draft already exists it is returned unchanged; otherwise a new empty draft is created.
886
+
887
+ [API Reference](https://loops.so/docs/api-reference/ensure-transactional-email-draft)
888
+
889
+ #### Parameters
890
+
891
+ | Name | Type | Required | Notes |
892
+ | ------------------ | ------ | -------- | ----- |
893
+ | `transactional_id` | string | Yes | |
894
+
895
+ #### Example
896
+
897
+ ```ruby
898
+ response = LoopsSdk::Transactional.ensure_draft(transactional_id: "clfq6dinn000yl70fgwwyp82l")
899
+ ```
900
+
901
+ ---
902
+
903
+ ### Transactional.publish()
904
+
905
+ Publish a transactional email's current draft. The draft becomes the published version and the draft is cleared.
906
+
907
+ [API Reference](https://loops.so/docs/api-reference/publish-transactional-email)
908
+
909
+ #### Parameters
910
+
911
+ | Name | Type | Required | Notes |
912
+ | ------------------ | ------ | -------- | ----- |
913
+ | `transactional_id` | string | Yes | |
914
+
915
+ #### Example
916
+
917
+ ```ruby
918
+ response = LoopsSdk::Transactional.publish(transactional_id: "clfq6dinn000yl70fgwwyp82l")
919
+ ```
920
+
921
+ ---
922
+
718
923
  ### Transactional.send()
719
924
 
720
925
  Send a transactional email to a contact. [Learn about sending transactional email](https://loops.so/docs/transactional/guide)
@@ -725,7 +930,7 @@ Send a transactional email to a contact. [Learn about sending transactional emai
725
930
 
726
931
  | Name | Type | Required | Notes |
727
932
  | ---------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
728
- | `transactional_id` | string | Yes | The ID of the transactional email to send. |
933
+ | `transactional_id` | string | Yes | The ID of the transactional email to send. |
729
934
  | `email` | string | Yes | The email address of the recipient. |
730
935
  | `add_to_audience` | boolean | No | If `true`, a contact will be created in your audience using the `email` value (if a matching contact doesn't already exist). |
731
936
  | `data_variables` | object | No | An object containing data as defined by the data variables added to the transactional email template.<br />Values can be of type `string` or `number`. |
@@ -808,11 +1013,37 @@ If there is a problem with the request, a descriptive error message will be retu
808
1013
 
809
1014
  ---
810
1015
 
811
- ### Transactional.list()
1016
+ ### DedicatedSendingIps.list()
812
1017
 
813
- Get a list of published transactional emails.
1018
+ Get Loops' dedicated sending IP addresses.
814
1019
 
815
- [API Reference](https://loops.so/docs/api-reference/list-transactional-emails)
1020
+ [API Reference](https://loops.so/docs/api-reference/list-dedicated-sending-ips)
1021
+
1022
+ #### Parameters
1023
+
1024
+ None
1025
+
1026
+ #### Example
1027
+
1028
+ ```ruby
1029
+ response = LoopsSdk::DedicatedSendingIps.list
1030
+ ```
1031
+
1032
+ #### Response
1033
+
1034
+ Returns an array of IP address strings.
1035
+
1036
+ ```json
1037
+ ["1.2.3.4", "5.6.7.8"]
1038
+ ```
1039
+
1040
+ ---
1041
+
1042
+ ### Themes.list()
1043
+
1044
+ List email themes.
1045
+
1046
+ [API Reference](https://loops.so/docs/api-reference/list-themes)
816
1047
 
817
1048
  #### Parameters
818
1049
 
@@ -824,47 +1055,578 @@ Get a list of published transactional emails.
824
1055
  #### Example
825
1056
 
826
1057
  ```ruby
827
- response = LoopsSdk::Transactional.list
1058
+ response = LoopsSdk::Themes.list
1059
+
1060
+ response = LoopsSdk::Themes.list(perPage: 15, cursor: "cursor_value")
1061
+ ```
1062
+
1063
+ ---
1064
+
1065
+ ### Themes.get()
1066
+
1067
+ Get a single theme by ID.
1068
+
1069
+ [API Reference](https://loops.so/docs/api-reference/get-theme)
1070
+
1071
+ #### Parameters
1072
+
1073
+ | Name | Type | Required | Notes |
1074
+ | ---------- | ------ | -------- | ----- |
1075
+ | `theme_id` | string | Yes | |
1076
+
1077
+ #### Example
1078
+
1079
+ ```ruby
1080
+ response = LoopsSdk::Themes.get(theme_id: "clo5p8q0r0132ntx6flkunw89")
1081
+ ```
1082
+
1083
+ ---
1084
+
1085
+ ### Components.list()
1086
+
1087
+ List email components.
1088
+
1089
+ [API Reference](https://loops.so/docs/api-reference/list-components)
1090
+
1091
+ #### Parameters
1092
+
1093
+ | Name | Type | Required | Notes |
1094
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1095
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1096
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1097
+
1098
+ #### Example
1099
+
1100
+ ```ruby
1101
+ response = LoopsSdk::Components.list
1102
+ ```
1103
+
1104
+ ---
1105
+
1106
+ ### Components.get()
1107
+
1108
+ Get a single component by ID.
1109
+
1110
+ [API Reference](https://loops.so/docs/api-reference/get-component)
1111
+
1112
+ #### Parameters
1113
+
1114
+ | Name | Type | Required | Notes |
1115
+ | -------------- | ------ | -------- | ----- |
1116
+ | `component_id` | string | Yes | |
1117
+
1118
+ #### Example
1119
+
1120
+ ```ruby
1121
+ response = LoopsSdk::Components.get(component_id: "clp6q9r1s0154ouy7gmlovx90")
1122
+ ```
1123
+
1124
+ ---
1125
+
1126
+ ### Campaigns.list()
1127
+
1128
+ List campaigns.
828
1129
 
829
- response = LoopsSdk::Transactional.list(perPage: 15)
1130
+ [API Reference](https://loops.so/docs/api-reference/list-campaigns)
1131
+
1132
+ #### Parameters
1133
+
1134
+ | Name | Type | Required | Notes |
1135
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1136
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1137
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1138
+
1139
+ #### Example
1140
+
1141
+ ```ruby
1142
+ response = LoopsSdk::Campaigns.list
1143
+ ```
1144
+
1145
+ ---
1146
+
1147
+ ### Campaigns.create()
1148
+
1149
+ Create a draft campaign. An empty email message is created automatically.
1150
+
1151
+ [API Reference](https://loops.so/docs/api-reference/create-campaign)
1152
+
1153
+ #### Parameters
1154
+
1155
+ | Name | Type | Required | Notes |
1156
+ | ---------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
1157
+ | `name` | string | Yes | |
1158
+ | `campaign_group_id` | string | No | The ID of the group to add this campaign to. |
1159
+ | `mailing_list_id` | string | No | The ID of the mailing list to send to. |
1160
+ | `audience_segment_id` | string | No | The ID of an audience segment. Setting this clears any `audience_filter`. |
1161
+ | `audience_filter` | object | No | A tree of audience conditions. See the API reference for the filter schema. |
1162
+ | `scheduling` | object | No | When the campaign should send. Use `{ method: "now" }` or `{ method: "schedule", timestamp: "..." }`. |
1163
+
1164
+ #### Example
1165
+
1166
+ ```ruby
1167
+ response = LoopsSdk::Campaigns.create(name: "Spring announcement")
1168
+
1169
+ response = LoopsSdk::Campaigns.create(
1170
+ name: "Spring announcement",
1171
+ mailing_list_id: "cm06f5v0e45nf0ml5754o9cix",
1172
+ scheduling: { method: "schedule", timestamp: "2026-06-01T10:00:00Z" }
1173
+ )
1174
+ ```
1175
+
1176
+ ---
1177
+
1178
+ ### Campaigns.get()
1179
+
1180
+ Get a single campaign by ID.
1181
+
1182
+ [API Reference](https://loops.so/docs/api-reference/get-campaign)
1183
+
1184
+ #### Parameters
1185
+
1186
+ | Name | Type | Required | Notes |
1187
+ | ------------- | ------ | -------- | ----- |
1188
+ | `campaign_id` | string | Yes | |
1189
+
1190
+ #### Example
1191
+
1192
+ ```ruby
1193
+ response = LoopsSdk::Campaigns.get(campaign_id: "cln4o7p9q0110msw5ekjtmv78")
1194
+ ```
1195
+
1196
+ ---
1197
+
1198
+ ### Campaigns.update()
1199
+
1200
+ Update a draft campaign's name, group, audience, or scheduling.
1201
+
1202
+ [API Reference](https://loops.so/docs/api-reference/update-campaign)
1203
+
1204
+ #### Parameters
1205
+
1206
+ | Name | Type | Required | Notes |
1207
+ | --------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
1208
+ | `campaign_id` | string | Yes | |
1209
+ | `name` | string | No | |
1210
+ | `campaign_group_id` | string | No | The ID of the group to move this campaign to. |
1211
+ | `mailing_list_id` | string | No | The ID of the mailing list to send to. |
1212
+ | `audience_segment_id` | string | No | The ID of an audience segment. Setting this clears any `audience_filter`. |
1213
+ | `audience_filter` | object | No | A tree of audience conditions. See the API reference for the filter schema. |
1214
+ | `scheduling` | object | No | When the campaign should send. Use `{ method: "now" }` or `{ method: "schedule", timestamp: "..." }`. |
1215
+
1216
+ At least one field must be provided.
1217
+
1218
+ #### Example
1219
+
1220
+ ```ruby
1221
+ response = LoopsSdk::Campaigns.update(
1222
+ campaign_id: "cln4o7p9q0110msw5ekjtmv78",
1223
+ name: "Updated campaign name"
1224
+ )
1225
+ ```
1226
+
1227
+ ---
1228
+
1229
+ ### EmailMessages.get()
1230
+
1231
+ Get an email message, including its LMX content.
1232
+
1233
+ [API Reference](https://loops.so/docs/api-reference/get-email-message)
1234
+
1235
+ #### Parameters
1236
+
1237
+ | Name | Type | Required | Notes |
1238
+ | ------------------- | ------ | -------- | ----- |
1239
+ | `email_message_id` | string | Yes | |
1240
+
1241
+ #### Example
1242
+
1243
+ ```ruby
1244
+ response = LoopsSdk::EmailMessages.get(email_message_id: "cly8k3m0n0044jpx2bghepq45")
1245
+ ```
1246
+
1247
+ ---
1248
+
1249
+ ### EmailMessages.update()
1250
+
1251
+ Update an email message for a draft campaign.
1252
+
1253
+ [API Reference](https://loops.so/docs/api-reference/update-email-message)
1254
+
1255
+ #### Parameters
1256
+
1257
+ | Name | Type | Required | Notes |
1258
+ | ------------------------------ | ------ | -------- | ---------------------------------------------------------------------------------------------------------- |
1259
+ | `email_message_id` | string | Yes | |
1260
+ | `expected_revision_id` | string | No | The `contentRevisionId` from your last fetch. Required to avoid stale concurrent updates. |
1261
+ | `subject` | string | No | |
1262
+ | `preview_text` | string | No | |
1263
+ | `from_name` | string | No | |
1264
+ | `from_email` | string | No | Sender username without `@` or domain. The team's sending domain is appended automatically. |
1265
+ | `reply_to_email` | string | No | Must be empty or a valid email address. |
1266
+ | `cc_email` | string | No | CC email address. Requires the team to have CC/BCC enabled. |
1267
+ | `bcc_email` | string | No | BCC email address. Requires the team to have CC/BCC enabled. |
1268
+ | `language_code` | string | No | Language code for the email. Requires translation to be enabled for the team. |
1269
+ | `email_format` | string | No | The rendering format of the email. One of `styled` or `plain`. |
1270
+ | `lmx` | string | No | Email body serialized as LMX. Styles must be embedded in the LMX `<Style />` tag. |
1271
+ | `contact_properties_fallbacks` | object | No | Fallback values for contact properties. A null value deletes the fallback. |
1272
+ | `event_properties_fallbacks` | object | No | Fallback values for event properties. A null value deletes the fallback. |
1273
+ | `data_variables_fallbacks` | object | No | Fallback values for data variables. A null value deletes the fallback. |
1274
+
1275
+ #### Example
1276
+
1277
+ ```ruby
1278
+ response = LoopsSdk::EmailMessages.update(
1279
+ email_message_id: "cly8k3m0n0044jpx2bghepq45",
1280
+ expected_revision_id: "clm9n4o6p0088lrz4dijslt67",
1281
+ subject: "Spring announcement",
1282
+ preview_text: "See what's new",
1283
+ from_name: "Loops",
1284
+ from_email: "hello",
1285
+ lmx: "<Email><Style /></Email>"
1286
+ )
1287
+ ```
1288
+
1289
+ ---
1290
+
1291
+ ### EmailMessages.preview()
1292
+
1293
+ Send a test preview of an email message to one or more addresses.
1294
+
1295
+ [API Reference](https://loops.so/docs/api-reference/preview-email-message)
1296
+
1297
+ #### Parameters
1298
+
1299
+ | Name | Type | Required | Notes |
1300
+ | -------------------- | -------- | -------- | --------------------------------------------------------------------------------- |
1301
+ | `email_message_id` | string | Yes | |
1302
+ | `emails` | string[] | Yes | One or more addresses to send the preview to. |
1303
+ | `contact_properties` | object | No | Contact property values to render. Accepted for campaign and workflow previews. |
1304
+ | `event_properties` | object | No | Event property values to render. Accepted for workflow previews only. |
1305
+ | `data_variables` | object | No | Transactional data variables to render. Accepted for transactional previews only. |
1306
+
1307
+ #### Example
1308
+
1309
+ ```ruby
1310
+ response = LoopsSdk::EmailMessages.preview(
1311
+ email_message_id: "cly8k3m0n0044jpx2bghepq45",
1312
+ emails: ["test@example.com"],
1313
+ contact_properties: { firstName: "Alex" }
1314
+ )
1315
+ ```
1316
+
1317
+ ---
1318
+
1319
+ ### CampaignGroups.list()
1320
+
1321
+ List campaign groups.
1322
+
1323
+ [API Reference](https://loops.so/docs/api-reference/list-campaign-groups)
1324
+
1325
+ #### Parameters
1326
+
1327
+ | Name | Type | Required | Notes |
1328
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1329
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1330
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1331
+
1332
+ #### Example
1333
+
1334
+ ```ruby
1335
+ response = LoopsSdk::CampaignGroups.list
1336
+ ```
1337
+
1338
+ ---
1339
+
1340
+ ### CampaignGroups.create()
1341
+
1342
+ Create a campaign group.
1343
+
1344
+ [API Reference](https://loops.so/docs/api-reference/create-campaign-group)
1345
+
1346
+ #### Parameters
1347
+
1348
+ | Name | Type | Required | Notes |
1349
+ | ------------- | ------ | -------- | --------------------------------------- |
1350
+ | `name` | string | Yes | Cannot be the reserved name "Unsorted". |
1351
+ | `description` | string | No | An optional description for the group. |
1352
+
1353
+ #### Example
1354
+
1355
+ ```ruby
1356
+ response = LoopsSdk::CampaignGroups.create(name: "Newsletters", description: "Monthly updates")
1357
+ ```
1358
+
1359
+ ---
1360
+
1361
+ ### CampaignGroups.get()
1362
+
1363
+ Get a campaign group by ID.
1364
+
1365
+ [API Reference](https://loops.so/docs/api-reference/get-campaign-group)
1366
+
1367
+ #### Parameters
1368
+
1369
+ | Name | Type | Required | Notes |
1370
+ | ---- | ------ | -------- | ----- |
1371
+ | `campaign_group_id` | string | Yes | |
1372
+
1373
+ #### Example
1374
+
1375
+ ```ruby
1376
+ response = LoopsSdk::CampaignGroups.get(campaign_group_id: "clq7r0s2t0176pvz8hnmpwy01")
1377
+ ```
1378
+
1379
+ ---
1380
+
1381
+ ### CampaignGroups.update()
1382
+
1383
+ Update a campaign group's name or description.
1384
+
1385
+ [API Reference](https://loops.so/docs/api-reference/update-campaign-group)
1386
+
1387
+ #### Parameters
1388
+
1389
+ | Name | Type | Required | Notes |
1390
+ | ------------- | ------ | -------- | --------------------------------------- |
1391
+ | `campaign_group_id` | string | Yes | |
1392
+ | `name` | string | No | Cannot be the reserved name "Unsorted". |
1393
+ | `description` | string | No | |
1394
+
1395
+ At least one field must be provided.
1396
+
1397
+ #### Example
1398
+
1399
+ ```ruby
1400
+ response = LoopsSdk::CampaignGroups.update(
1401
+ campaign_group_id: "clq7r0s2t0176pvz8hnmpwy01",
1402
+ name: "Updated name"
1403
+ )
1404
+ ```
1405
+
1406
+ ---
1407
+
1408
+ ### AudienceSegments.list()
1409
+
1410
+ List audience segments.
1411
+
1412
+ [API Reference](https://loops.so/docs/api-reference/list-audience-segments)
1413
+
1414
+ #### Parameters
1415
+
1416
+ | Name | Type | Required | Notes |
1417
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1418
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1419
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1420
+
1421
+ #### Example
1422
+
1423
+ ```ruby
1424
+ response = LoopsSdk::AudienceSegments.list
1425
+ ```
1426
+
1427
+ ---
1428
+
1429
+ ### AudienceSegments.get()
1430
+
1431
+ Get an audience segment by ID.
1432
+
1433
+ [API Reference](https://loops.so/docs/api-reference/get-audience-segment)
1434
+
1435
+ #### Parameters
1436
+
1437
+ | Name | Type | Required | Notes |
1438
+ | ---- | ------ | -------- | ----- |
1439
+ | `audience_segment_id` | string | Yes | |
1440
+
1441
+ #### Example
1442
+
1443
+ ```ruby
1444
+ response = LoopsSdk::AudienceSegments.get(audience_segment_id: "clr8s1t3u0198qw09iotqzx12")
1445
+ ```
1446
+
1447
+ ---
1448
+
1449
+ ### Workflows.list()
1450
+
1451
+ List workflows.
1452
+
1453
+ [API Reference](https://loops.so/docs/api-reference/list-workflows)
1454
+
1455
+ #### Parameters
1456
+
1457
+ | Name | Type | Required | Notes |
1458
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1459
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1460
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1461
+
1462
+ #### Example
1463
+
1464
+ ```ruby
1465
+ response = LoopsSdk::Workflows.list
1466
+ ```
1467
+
1468
+ ---
1469
+
1470
+ ### Workflows.get()
1471
+
1472
+ Get a simplified workflow graph.
1473
+
1474
+ [API Reference](https://loops.so/docs/api-reference/get-workflow)
1475
+
1476
+ #### Parameters
1477
+
1478
+ | Name | Type | Required | Notes |
1479
+ | ---- | ------ | -------- | ----- |
1480
+ | `workflow_id` | string | Yes | |
1481
+
1482
+ #### Example
1483
+
1484
+ ```ruby
1485
+ response = LoopsSdk::Workflows.get(workflow_id: "cls9t2u4v0210rx20jpuary23")
1486
+ ```
1487
+
1488
+ ---
1489
+
1490
+ ### Workflows.get_node()
1491
+
1492
+ Get detailed data for a single workflow node.
1493
+
1494
+ [API Reference](https://loops.so/docs/api-reference/get-workflow-node)
1495
+
1496
+ #### Parameters
1497
+
1498
+ | Name | Type | Required | Notes |
1499
+ | ------------ | ------ | -------- | ----- |
1500
+ | `workflow_id`| string | Yes | |
1501
+ | `node_id` | string | Yes | |
1502
+
1503
+ #### Example
1504
+
1505
+ ```ruby
1506
+ response = LoopsSdk::Workflows.get_node(workflow_id: "cls9t2u4v0210rx20jpuary23", node_id: "clt0u3v5w0232sy31kqvbzs34")
1507
+ ```
1508
+
1509
+ ---
1510
+
1511
+ ### TransactionalGroups.list()
1512
+
1513
+ List transactional groups.
1514
+
1515
+ [API Reference](https://loops.so/docs/api-reference/list-transactional-groups)
1516
+
1517
+ #### Parameters
1518
+
1519
+ | Name | Type | Required | Notes |
1520
+ | --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
1521
+ | `perPage` | integer | No | How many results to return per page. Must be between 10 and 50. Defaults to 20 if omitted. |
1522
+ | `cursor` | string | No | A cursor, to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response. |
1523
+
1524
+ #### Example
1525
+
1526
+ ```ruby
1527
+ response = LoopsSdk::TransactionalGroups.list
1528
+ ```
1529
+
1530
+ ---
1531
+
1532
+ ### TransactionalGroups.create()
1533
+
1534
+ Create a transactional group.
1535
+
1536
+ [API Reference](https://loops.so/docs/api-reference/create-transactional-group)
1537
+
1538
+ #### Parameters
1539
+
1540
+ | Name | Type | Required | Notes |
1541
+ | ------------- | ------ | -------- | --------------------------------------- |
1542
+ | `name` | string | Yes | Cannot be the reserved name "Unsorted". |
1543
+ | `description` | string | No | An optional description for the group. |
1544
+
1545
+ #### Example
1546
+
1547
+ ```ruby
1548
+ response = LoopsSdk::TransactionalGroups.create(name: "Account emails")
1549
+ ```
1550
+
1551
+ ---
1552
+
1553
+ ### TransactionalGroups.get()
1554
+
1555
+ Get a transactional group by ID.
1556
+
1557
+ [API Reference](https://loops.so/docs/api-reference/get-transactional-group)
1558
+
1559
+ #### Parameters
1560
+
1561
+ | Name | Type | Required | Notes |
1562
+ | ---- | ------ | -------- | ----- |
1563
+ | `transactional_group_id` | string | Yes | |
1564
+
1565
+ #### Example
1566
+
1567
+ ```ruby
1568
+ response = LoopsSdk::TransactionalGroups.get(transactional_group_id: "clv2w3x4y0288xbb0kqrsuv67")
1569
+ ```
1570
+
1571
+ ---
1572
+
1573
+ ### TransactionalGroups.update()
1574
+
1575
+ Update a transactional group's name or description.
1576
+
1577
+ [API Reference](https://loops.so/docs/api-reference/update-transactional-group)
1578
+
1579
+ #### Parameters
1580
+
1581
+ | Name | Type | Required | Notes |
1582
+ | ------------- | ------ | -------- | --------------------------------------- |
1583
+ | `transactional_group_id` | string | Yes | |
1584
+ | `name` | string | No | Cannot be the reserved name "Unsorted". |
1585
+ | `description` | string | No | |
1586
+
1587
+ At least one field must be provided.
1588
+
1589
+ #### Example
1590
+
1591
+ ```ruby
1592
+ response = LoopsSdk::TransactionalGroups.update(
1593
+ transactional_group_id: "clv2w3x4y0288xbb0kqrsuv67",
1594
+ name: "Updated name"
1595
+ )
1596
+ ```
1597
+
1598
+ ---
1599
+
1600
+ ### Uploads.upload()
1601
+
1602
+ Upload an image file for use in LMX email content.
1603
+
1604
+ Supported image types: JPEG, PNG, GIF, and WebP (max 4 MB). MIME type is detected from file contents, or pass `content_type:` to override.
1605
+
1606
+ [API Reference](https://loops.so/docs/api-reference/create-upload)
1607
+
1608
+ #### Parameters
1609
+
1610
+ | Name | Type | Required | Notes |
1611
+ | --------------- | ------ | -------- | -------------------------------------------------------------------------------------------- |
1612
+ | `path` | string | Yes | Path to the image file on disk. |
1613
+ | `content_type` | string | No | MIME type override. Supported: `image/jpeg`, `image/png`, `image/gif`, `image/webp`. |
1614
+
1615
+ #### Example
1616
+
1617
+ ```ruby
1618
+ response = LoopsSdk::Uploads.upload(path: "./header.png")
1619
+
1620
+ # Use the returned URL in LMX
1621
+ lmx = %(<Image src="#{response['finalUrl']}" alt="Header" />)
830
1622
  ```
831
1623
 
832
1624
  #### Response
833
1625
 
834
1626
  ```json
835
1627
  {
836
- "pagination": {
837
- "totalResults": 23,
838
- "returnedResults": 20,
839
- "perPage": 20,
840
- "totalPages": 2,
841
- "nextCursor": "clyo0q4wo01p59fsecyxqsh38",
842
- "nextPage": "https://app.loops.so/api/v1/transactional?cursor=clyo0q4wo01p59fsecyxqsh38&perPage=20"
843
- },
844
- "data": [
845
- {
846
- "id": "clfn0k1yg001imo0fdeqg30i8",
847
- "lastUpdated": "2023-11-06T17:48:07.249Z",
848
- "dataVariables": []
849
- },
850
- {
851
- "id": "cll42l54f20i1la0lfooe3z12",
852
- "lastUpdated": "2025-02-02T02:56:28.845Z",
853
- "dataVariables": [
854
- "confirmationUrl"
855
- ]
856
- },
857
- {
858
- "id": "clw6rbuwp01rmeiyndm80155l",
859
- "lastUpdated": "2024-05-14T19:02:52.000Z",
860
- "dataVariables": [
861
- "firstName",
862
- "lastName",
863
- "inviteLink"
864
- ]
865
- },
866
- ...
867
- ]
1628
+ "emailAssetId": "clu1v4w6x0254tz42lrcwat45",
1629
+ "finalUrl": "https://cdn.loops.so/clu1v4w6x0254tz42lrcwat45.png"
868
1630
  }
869
1631
  ```
870
1632
 
@@ -878,4 +1640,4 @@ Run tests with `bundle exec rspec`.
878
1640
 
879
1641
  ## Contributing
880
1642
 
881
- Bug reports and pull requests are welcome. Please read our [Contributing Guidelines](CONTRIBUTING.md).
1643
+ Bug reports and pull requests are welcome. Please read our [Contributing Guidelines](CONTRIBUTING.md).
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class AudienceSegments < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/audience-segments", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def get(audience_segment_id:)
11
+ make_request(method: :get, path: "v1/audience-segments/#{audience_segment_id}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -7,13 +7,13 @@ module LoopsSdk
7
7
 
8
8
  def handle_response(response)
9
9
  case response.status
10
- when 200
10
+ when 200, 201
11
11
  JSON.parse(response.body)
12
12
  when 429
13
13
  limit = response.headers["x-ratelimit-limit"]
14
14
  remaining = response.headers["x-ratelimit-remaining"]
15
15
  raise RateLimitError.new(limit, remaining)
16
- when 400, 404, 405, 409, 500
16
+ when 400, 401, 404, 405, 409, 413, 422, 500
17
17
  raise APIError.new(response.status, response.body)
18
18
  else
19
19
  raise APIError.new(response.status, "Unexpected error occurred")
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class CampaignGroups < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/campaign-groups", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def create(name:, description: nil)
11
+ body = { name: name, description: description }.compact
12
+ make_request(method: :post, path: "v1/campaign-groups", body: body)
13
+ end
14
+
15
+ def get(campaign_group_id:)
16
+ make_request(method: :get, path: "v1/campaign-groups/#{campaign_group_id}")
17
+ end
18
+
19
+ def update(campaign_group_id:, name: nil, description: nil)
20
+ body = { name: name, description: description }.compact
21
+ make_request(method: :post, path: "v1/campaign-groups/#{campaign_group_id}", body: body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class Campaigns < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/campaigns", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def create(name:, campaign_group_id: nil, mailing_list_id: nil, audience_segment_id: nil, audience_filter: nil, scheduling: nil)
11
+ body = {
12
+ name: name,
13
+ campaignGroupId: campaign_group_id,
14
+ mailingListId: mailing_list_id,
15
+ audienceSegmentId: audience_segment_id,
16
+ audienceFilter: audience_filter,
17
+ scheduling: scheduling
18
+ }.compact
19
+ make_request(method: :post, path: "v1/campaigns", body: body)
20
+ end
21
+
22
+ def get(campaign_id:)
23
+ make_request(method: :get, path: "v1/campaigns/#{campaign_id}")
24
+ end
25
+
26
+ def update(campaign_id:, name: nil, campaign_group_id: nil, mailing_list_id: nil, audience_segment_id: nil, audience_filter: nil, scheduling: nil)
27
+ body = {
28
+ name: name,
29
+ campaignGroupId: campaign_group_id,
30
+ mailingListId: mailing_list_id,
31
+ audienceSegmentId: audience_segment_id,
32
+ audienceFilter: audience_filter,
33
+ scheduling: scheduling
34
+ }.compact
35
+ make_request(method: :post, path: "v1/campaigns/#{campaign_id}", body: body)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class Components < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/components", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def get(component_id:)
11
+ make_request(method: :get, path: "v1/components/#{component_id}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class DedicatedSendingIps < Base
5
+ class << self
6
+ def list
7
+ make_request(method: :get, path: "v1/dedicated-sending-ips")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class EmailMessages < Base
5
+ class << self
6
+ def get(email_message_id:)
7
+ make_request(method: :get, path: "v1/email-messages/#{email_message_id}")
8
+ end
9
+
10
+ def update(
11
+ email_message_id:,
12
+ expected_revision_id: nil,
13
+ subject: nil,
14
+ preview_text: nil,
15
+ from_name: nil,
16
+ from_email: nil,
17
+ reply_to_email: nil,
18
+ cc_email: nil,
19
+ bcc_email: nil,
20
+ language_code: nil,
21
+ email_format: nil,
22
+ lmx: nil,
23
+ contact_properties_fallbacks: nil,
24
+ event_properties_fallbacks: nil,
25
+ data_variables_fallbacks: nil
26
+ )
27
+ body = {
28
+ expectedRevisionId: expected_revision_id,
29
+ subject: subject,
30
+ previewText: preview_text,
31
+ fromName: from_name,
32
+ fromEmail: from_email,
33
+ replyToEmail: reply_to_email,
34
+ ccEmail: cc_email,
35
+ bccEmail: bcc_email,
36
+ languageCode: language_code,
37
+ emailFormat: email_format,
38
+ lmx: lmx,
39
+ contactPropertiesFallbacks: contact_properties_fallbacks,
40
+ eventPropertiesFallbacks: event_properties_fallbacks,
41
+ dataVariablesFallbacks: data_variables_fallbacks
42
+ }.compact
43
+ make_request(method: :post, path: "v1/email-messages/#{email_message_id}", body: body)
44
+ end
45
+
46
+ def preview(email_message_id:, emails:, contact_properties: nil, event_properties: nil, data_variables: nil)
47
+ body = {
48
+ emails: emails,
49
+ contactProperties: contact_properties,
50
+ eventProperties: event_properties,
51
+ dataVariables: data_variables
52
+ }.compact
53
+ make_request(method: :post, path: "v1/email-messages/#{email_message_id}/preview", body: body)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class Themes < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/themes", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def get(theme_id:)
11
+ make_request(method: :get, path: "v1/themes/#{theme_id}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -4,8 +4,31 @@ module LoopsSdk
4
4
  class Transactional < Base
5
5
  class << self
6
6
  def list(perPage: 20, cursor: nil)
7
- make_request(method: :get, path: "v1/transactional", params: { perPage: perPage, cursor: cursor })
7
+ make_request(method: :get, path: "v1/transactional-emails", params: { perPage: perPage, cursor: cursor })
8
8
  end
9
+
10
+ def create(name:, transactional_group_id: nil)
11
+ body = { name: name, transactionalGroupId: transactional_group_id }.compact
12
+ make_request(method: :post, path: "v1/transactional-emails", body: body)
13
+ end
14
+
15
+ def get(transactional_id:)
16
+ make_request(method: :get, path: "v1/transactional-emails/#{transactional_id}")
17
+ end
18
+
19
+ def update(transactional_id:, name: nil, transactional_group_id: nil)
20
+ body = { name: name, transactionalGroupId: transactional_group_id }.compact
21
+ make_request(method: :post, path: "v1/transactional-emails/#{transactional_id}", body: body)
22
+ end
23
+
24
+ def ensure_draft(transactional_id:)
25
+ make_request(method: :post, path: "v1/transactional-emails/#{transactional_id}/draft")
26
+ end
27
+
28
+ def publish(transactional_id:)
29
+ make_request(method: :post, path: "v1/transactional-emails/#{transactional_id}/publish")
30
+ end
31
+
9
32
  def send(transactional_id:, email:, add_to_audience: false, data_variables: {}, attachments: [], headers: {})
10
33
  attachments = attachments.map do |attachment|
11
34
  attachment.transform_keys { |key| key == :content_type ? :contentType : key }
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class TransactionalGroups < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/transactional-groups", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def create(name:, description: nil)
11
+ body = { name: name, description: description }.compact
12
+ make_request(method: :post, path: "v1/transactional-groups", body: body)
13
+ end
14
+
15
+ def get(transactional_group_id:)
16
+ make_request(method: :get, path: "v1/transactional-groups/#{transactional_group_id}")
17
+ end
18
+
19
+ def update(transactional_group_id:, name: nil, description: nil)
20
+ body = { name: name, description: description }.compact
21
+ make_request(method: :post, path: "v1/transactional-groups/#{transactional_group_id}", body: body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class Uploads < Base
5
+ SUPPORTED_CONTENT_TYPES = %w[image/jpeg image/png image/gif image/webp].freeze
6
+
7
+ class << self
8
+ def upload(path:, content_type: nil)
9
+ data = File.binread(path)
10
+ content_type ||= detect_content_type(data)
11
+ validate_content_type!(content_type)
12
+
13
+ create_response = make_request(
14
+ method: :post,
15
+ path: "v1/uploads",
16
+ body: { contentType: content_type, contentLength: data.bytesize }
17
+ )
18
+
19
+ put_to_presigned_url(create_response["presignedUrl"], data, content_type)
20
+
21
+ make_request(
22
+ method: :post,
23
+ path: "v1/uploads/#{create_response["emailAssetId"]}/complete"
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ def detect_content_type(data)
30
+ if data.start_with?("\xFF\xD8\xFF".b)
31
+ "image/jpeg"
32
+ elsif data.start_with?("\x89PNG\r\n\x1a\n".b)
33
+ "image/png"
34
+ elsif data.start_with?("GIF87a".b, "GIF89a".b)
35
+ "image/gif"
36
+ elsif data.bytesize >= 12 && data[0, 4] == "RIFF".b && data[8, 4] == "WEBP".b
37
+ "image/webp"
38
+ else
39
+ raise ArgumentError,
40
+ "Unable to detect image type. Pass content_type: explicitly. " \
41
+ "Supported types: #{SUPPORTED_CONTENT_TYPES.join(', ')}."
42
+ end
43
+ end
44
+
45
+ def validate_content_type!(content_type)
46
+ return if SUPPORTED_CONTENT_TYPES.include?(content_type)
47
+
48
+ raise ArgumentError,
49
+ "Unsupported content_type: #{content_type}. " \
50
+ "Supported types: #{SUPPORTED_CONTENT_TYPES.join(', ')}."
51
+ end
52
+
53
+ def put_to_presigned_url(url, data, content_type)
54
+ response = Faraday.put(url) do |req|
55
+ req.headers["Content-Type"] = content_type
56
+ req.headers["Content-Length"] = data.bytesize.to_s
57
+ req.body = data
58
+ end
59
+
60
+ return if response.success?
61
+
62
+ raise APIError.new(response.status, response.body)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LoopsSdk
4
- VERSION = "2.1.0"
4
+ VERSION = "2.3.0"
5
5
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class Workflows < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/workflows", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def get(workflow_id:)
11
+ make_request(method: :get, path: "v1/workflows/#{workflow_id}")
12
+ end
13
+
14
+ def get_node(workflow_id:, node_id:)
15
+ make_request(method: :get, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}")
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/loops_sdk.rb CHANGED
@@ -11,6 +11,16 @@ require_relative "loops_sdk/mailing_lists"
11
11
  require_relative "loops_sdk/transactional"
12
12
  require_relative "loops_sdk/contact_properties"
13
13
  require_relative "loops_sdk/api_key"
14
+ require_relative "loops_sdk/dedicated_sending_ips"
15
+ require_relative "loops_sdk/themes"
16
+ require_relative "loops_sdk/components"
17
+ require_relative "loops_sdk/campaigns"
18
+ require_relative "loops_sdk/campaign_groups"
19
+ require_relative "loops_sdk/email_messages"
20
+ require_relative "loops_sdk/uploads"
21
+ require_relative "loops_sdk/audience_segments"
22
+ require_relative "loops_sdk/workflows"
23
+ require_relative "loops_sdk/transactional_groups"
14
24
 
15
25
  module LoopsSdk
16
26
  class << self
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loops_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Rowden
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-08 00:00:00.000000000 Z
11
+ date: 2026-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -35,14 +35,24 @@ files:
35
35
  - README.md
36
36
  - lib/loops_sdk.rb
37
37
  - lib/loops_sdk/api_key.rb
38
+ - lib/loops_sdk/audience_segments.rb
38
39
  - lib/loops_sdk/base.rb
40
+ - lib/loops_sdk/campaign_groups.rb
41
+ - lib/loops_sdk/campaigns.rb
42
+ - lib/loops_sdk/components.rb
39
43
  - lib/loops_sdk/configuration.rb
40
44
  - lib/loops_sdk/contact_properties.rb
41
45
  - lib/loops_sdk/contacts.rb
46
+ - lib/loops_sdk/dedicated_sending_ips.rb
47
+ - lib/loops_sdk/email_messages.rb
42
48
  - lib/loops_sdk/events.rb
43
49
  - lib/loops_sdk/mailing_lists.rb
50
+ - lib/loops_sdk/themes.rb
44
51
  - lib/loops_sdk/transactional.rb
52
+ - lib/loops_sdk/transactional_groups.rb
53
+ - lib/loops_sdk/uploads.rb
45
54
  - lib/loops_sdk/version.rb
55
+ - lib/loops_sdk/workflows.rb
46
56
  homepage: https://loops.so/docs/api-reference
47
57
  licenses:
48
58
  - MIT