basecamp-sdk 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da0e2d03c80b5801b886da56a59535f99cb521bd8233a146720ee117f76954b8
4
- data.tar.gz: 4a3ea1075d88cac04a7dcc796f3c21d235aaffbb9d036be088321746c435c012
3
+ metadata.gz: '08447be78a091d1fd44c4ca85addf5d7f262f383e5d085d6765199362323d3de'
4
+ data.tar.gz: 538782581af8739afa7046e204b628fdbb6ed96f2822b92806b669c63b302708
5
5
  SHA512:
6
- metadata.gz: e5d5298402d8239d525cff7aeac7815254cb7a1a3a967ed6f29fe9fea581432f0c2e7b272edc52a866b457ec988afc11f3ff6dbb48977b5e2ac716af8bbdb46a
7
- data.tar.gz: f37ba99c63d4fb9836fe9fc5e6e717e0166f97892069da141f2be746ac95c8e15629319fb1deb4c23cdd4a7ee30d1ac84a83bf183e19930c9d394e47995704e6
6
+ metadata.gz: 90a91f187db4d1c0213169fba7c033a3da6f7efc5e2ef71c25318e1cbef39e2d3b7383477fd804257f7148ea44625f068658a1b1d6339585cda73a2a7f34a798
7
+ data.tar.gz: 052e7117c71e54bd7a415266b444e48e2344bc08b16475841e43934bdb7257c51cd283de7806f9b7893210611a778d36be3053dc3d5bee689f5107677e6cbc91
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module Basecamp
4
5
  # Main client for the Basecamp API.
5
6
  #
@@ -188,6 +189,16 @@ module Basecamp
188
189
  @parent.http.post_raw(account_path(path), body: body, content_type: content_type)
189
190
  end
190
191
 
192
+ # Performs a PUT request with raw binary data scoped to this account.
193
+ # Used for multipart uploads (e.g., account logo).
194
+ # @param path [String] URL path (without account prefix)
195
+ # @param body [String, IO] raw binary data
196
+ # @param content_type [String] MIME content type
197
+ # @return [Response]
198
+ def put_raw(path, body:, content_type:)
199
+ @parent.http.put_raw(account_path(path), body: body, content_type: content_type)
200
+ end
201
+
191
202
  # Fetches all pages of a paginated resource.
192
203
  # @param path [String] URL path (without account prefix)
193
204
  # @param params [Hash] query parameters
@@ -320,6 +331,11 @@ module Basecamp
320
331
  service(:todosets) { Services::TodosetsService.new(self) }
321
332
  end
322
333
 
334
+ # @return [Services::HillChartsService]
335
+ def hill_charts
336
+ service(:hill_charts) { Services::HillChartsService.new(self) }
337
+ end
338
+
323
339
  # @return [Services::TodolistsService]
324
340
  def todolists
325
341
  service(:todolists) { Services::TodolistsService.new(self) }
@@ -500,6 +516,26 @@ module Basecamp
500
516
  service(:boosts) { Services::BoostsService.new(self) }
501
517
  end
502
518
 
519
+ # @return [Services::AccountService]
520
+ def account
521
+ service(:account) { Services::AccountService.new(self) }
522
+ end
523
+
524
+ # @return [Services::GaugesService]
525
+ def gauges
526
+ service(:gauges) { Services::GaugesService.new(self) }
527
+ end
528
+
529
+ # @return [Services::MyAssignmentsService]
530
+ def my_assignments
531
+ service(:my_assignments) { Services::MyAssignmentsService.new(self) }
532
+ end
533
+
534
+ # @return [Services::MyNotificationsService]
535
+ def my_notifications
536
+ service(:my_notifications) { Services::MyNotificationsService.new(self) }
537
+ end
538
+
503
539
  # @!endgroup
504
540
 
505
541
  private
@@ -1,8 +1,61 @@
1
1
  {
2
2
  "$schema": "https://basecamp.com/schemas/sdk-metadata.json",
3
3
  "version": "1.0.0",
4
- "generated": "2026-03-16T01:24:22Z",
4
+ "generated": "2026-03-25T06:39:18Z",
5
5
  "operations": {
6
+ "GetAccount": {
7
+ "retry": {
8
+ "maxAttempts": 3,
9
+ "baseDelayMs": 1000,
10
+ "backoff": "exponential",
11
+ "retryOn": [
12
+ 429,
13
+ 503
14
+ ]
15
+ }
16
+ },
17
+ "UpdateAccountLogo": {
18
+ "retry": {
19
+ "maxAttempts": 2,
20
+ "baseDelayMs": 1000,
21
+ "backoff": "exponential",
22
+ "retryOn": [
23
+ 429,
24
+ 503
25
+ ]
26
+ },
27
+ "idempotent": {
28
+ "natural": true
29
+ }
30
+ },
31
+ "RemoveAccountLogo": {
32
+ "retry": {
33
+ "maxAttempts": 2,
34
+ "baseDelayMs": 1000,
35
+ "backoff": "exponential",
36
+ "retryOn": [
37
+ 429,
38
+ 503
39
+ ]
40
+ },
41
+ "idempotent": {
42
+ "natural": true
43
+ }
44
+ },
45
+ "UpdateAccountName": {
46
+ "retry": {
47
+ "maxAttempts": 2,
48
+ "baseDelayMs": 1000,
49
+ "backoff": "exponential",
50
+ "retryOn": [
51
+ 429,
52
+ 503
53
+ ]
54
+ },
55
+ "idempotent": {
56
+ "natural": true
57
+ }
58
+ },
6
59
  "CreateAttachment": {
7
60
  "retry": {
8
61
  "maxAttempts": 3,
@@ -750,6 +803,45 @@
750
803
  "natural": true
751
804
  }
752
805
  },
806
+ "GetGaugeNeedle": {
807
+ "retry": {
808
+ "maxAttempts": 3,
809
+ "baseDelayMs": 1000,
810
+ "backoff": "exponential",
811
+ "retryOn": [
812
+ 429,
813
+ 503
814
+ ]
815
+ }
816
+ },
817
+ "UpdateGaugeNeedle": {
818
+ "retry": {
819
+ "maxAttempts": 2,
820
+ "baseDelayMs": 1000,
821
+ "backoff": "exponential",
822
+ "retryOn": [
823
+ 429,
824
+ 503
825
+ ]
826
+ },
827
+ "idempotent": {
828
+ "natural": true
829
+ }
830
+ },
831
+ "DestroyGaugeNeedle": {
832
+ "retry": {
833
+ "maxAttempts": 2,
834
+ "baseDelayMs": 1000,
835
+ "backoff": "exponential",
836
+ "retryOn": [
837
+ 429,
838
+ 503
839
+ ]
840
+ },
841
+ "idempotent": {
842
+ "natural": true
843
+ }
844
+ },
753
845
  "GetForward": {
754
846
  "retry": {
755
847
  "maxAttempts": 3,
@@ -826,6 +918,17 @@
826
918
  "maxPageSize": 50
827
919
  }
828
920
  },
921
+ "ListLineupMarkers": {
922
+ "retry": {
923
+ "maxAttempts": 3,
924
+ "baseDelayMs": 1000,
925
+ "backoff": "exponential",
926
+ "retryOn": [
927
+ 429,
928
+ 503
929
+ ]
930
+ }
931
+ },
829
932
  "CreateLineupMarker": {
830
933
  "retry": {
831
934
  "maxAttempts": 2,
@@ -928,6 +1031,64 @@
928
1031
  "natural": true
929
1032
  }
930
1033
  },
1034
+ "GetMyAssignments": {
1035
+ "retry": {
1036
+ "maxAttempts": 3,
1037
+ "baseDelayMs": 1000,
1038
+ "backoff": "exponential",
1039
+ "retryOn": [
1040
+ 429,
1041
+ 503
1042
+ ]
1043
+ }
1044
+ },
1045
+ "GetMyCompletedAssignments": {
1046
+ "retry": {
1047
+ "maxAttempts": 3,
1048
+ "baseDelayMs": 1000,
1049
+ "backoff": "exponential",
1050
+ "retryOn": [
1051
+ 429,
1052
+ 503
1053
+ ]
1054
+ }
1055
+ },
1056
+ "GetMyDueAssignments": {
1057
+ "retry": {
1058
+ "maxAttempts": 3,
1059
+ "baseDelayMs": 1000,
1060
+ "backoff": "exponential",
1061
+ "retryOn": [
1062
+ 429,
1063
+ 503
1064
+ ]
1065
+ }
1066
+ },
1067
+ "GetMyPreferences": {
1068
+ "retry": {
1069
+ "maxAttempts": 3,
1070
+ "baseDelayMs": 1000,
1071
+ "backoff": "exponential",
1072
+ "retryOn": [
1073
+ 429,
1074
+ 503
1075
+ ]
1076
+ }
1077
+ },
1078
+ "UpdateMyPreferences": {
1079
+ "retry": {
1080
+ "maxAttempts": 2,
1081
+ "baseDelayMs": 1000,
1082
+ "backoff": "exponential",
1083
+ "retryOn": [
1084
+ 429,
1085
+ 503
1086
+ ]
1087
+ },
1088
+ "idempotent": {
1089
+ "natural": true
1090
+ }
1091
+ },
931
1092
  "GetMyProfile": {
932
1093
  "retry": {
933
1094
  "maxAttempts": 3,
@@ -939,6 +1100,20 @@
939
1100
  ]
940
1101
  }
941
1102
  },
1103
+ "UpdateMyProfile": {
1104
+ "retry": {
1105
+ "maxAttempts": 3,
1106
+ "baseDelayMs": 1000,
1107
+ "backoff": "exponential",
1108
+ "retryOn": [
1109
+ 429,
1110
+ 503
1111
+ ]
1112
+ },
1113
+ "idempotent": {
1114
+ "natural": true
1115
+ }
1116
+ },
942
1117
  "GetQuestionReminders": {
943
1118
  "retry": {
944
1119
  "maxAttempts": 3,
@@ -954,6 +1129,31 @@
954
1129
  "maxPageSize": 50
955
1130
  }
956
1131
  },
1132
+ "GetMyNotifications": {
1133
+ "retry": {
1134
+ "maxAttempts": 3,
1135
+ "baseDelayMs": 1000,
1136
+ "backoff": "exponential",
1137
+ "retryOn": [
1138
+ 429,
1139
+ 503
1140
+ ]
1141
+ }
1142
+ },
1143
+ "MarkAsRead": {
1144
+ "retry": {
1145
+ "maxAttempts": 2,
1146
+ "baseDelayMs": 1000,
1147
+ "backoff": "exponential",
1148
+ "retryOn": [
1149
+ 429,
1150
+ 503
1151
+ ]
1152
+ },
1153
+ "idempotent": {
1154
+ "natural": true
1155
+ }
1156
+ },
957
1157
  "ListPeople": {
958
1158
  "retry": {
959
1159
  "maxAttempts": 3,
@@ -981,6 +1181,42 @@
981
1181
  ]
982
1182
  }
983
1183
  },
1184
+ "GetOutOfOffice": {
1185
+ "retry": {
1186
+ "maxAttempts": 3,
1187
+ "baseDelayMs": 1000,
1188
+ "backoff": "exponential",
1189
+ "retryOn": [
1190
+ 429,
1191
+ 503
1192
+ ]
1193
+ }
1194
+ },
1195
+ "EnableOutOfOffice": {
1196
+ "retry": {
1197
+ "maxAttempts": 2,
1198
+ "baseDelayMs": 1000,
1199
+ "backoff": "exponential",
1200
+ "retryOn": [
1201
+ 429,
1202
+ 503
1203
+ ]
1204
+ }
1205
+ },
1206
+ "DisableOutOfOffice": {
1207
+ "retry": {
1208
+ "maxAttempts": 2,
1209
+ "baseDelayMs": 1000,
1210
+ "backoff": "exponential",
1211
+ "retryOn": [
1212
+ 429,
1213
+ 503
1214
+ ]
1215
+ },
1216
+ "idempotent": {
1217
+ "natural": true
1218
+ }
1219
+ },
984
1220
  "ListProjects": {
985
1221
  "retry": {
986
1222
  "maxAttempts": 3,
@@ -1063,6 +1299,47 @@
1063
1299
  "natural": true
1064
1300
  }
1065
1301
  },
1302
+ "ToggleGauge": {
1303
+ "retry": {
1304
+ "maxAttempts": 2,
1305
+ "baseDelayMs": 1000,
1306
+ "backoff": "exponential",
1307
+ "retryOn": [
1308
+ 429,
1309
+ 503
1310
+ ]
1311
+ },
1312
+ "idempotent": {
1313
+ "natural": true
1314
+ }
1315
+ },
1316
+ "ListGaugeNeedles": {
1317
+ "retry": {
1318
+ "maxAttempts": 3,
1319
+ "baseDelayMs": 1000,
1320
+ "backoff": "exponential",
1321
+ "retryOn": [
1322
+ 429,
1323
+ 503
1324
+ ]
1325
+ },
1326
+ "pagination": {
1327
+ "style": "link",
1328
+ "totalCountHeader": "X-Total-Count",
1329
+ "maxPageSize": 50
1330
+ }
1331
+ },
1332
+ "CreateGaugeNeedle": {
1333
+ "retry": {
1334
+ "maxAttempts": 2,
1335
+ "baseDelayMs": 1000,
1336
+ "backoff": "exponential",
1337
+ "retryOn": [
1338
+ 429,
1339
+ 503
1340
+ ]
1341
+ }
1342
+ },
1066
1343
  "ListProjectPeople": {
1067
1344
  "retry": {
1068
1345
  "maxAttempts": 3,
@@ -1617,6 +1894,22 @@
1617
1894
  "natural": true
1618
1895
  }
1619
1896
  },
1897
+ "ListGauges": {
1898
+ "retry": {
1899
+ "maxAttempts": 3,
1900
+ "baseDelayMs": 1000,
1901
+ "backoff": "exponential",
1902
+ "retryOn": [
1903
+ 429,
1904
+ 503
1905
+ ]
1906
+ },
1907
+ "pagination": {
1908
+ "style": "link",
1909
+ "totalCountHeader": "X-Total-Count",
1910
+ "maxPageSize": 50
1911
+ }
1912
+ },
1620
1913
  "GetProgressReport": {
1621
1914
  "retry": {
1622
1915
  "maxAttempts": 3,
@@ -2117,6 +2410,31 @@
2117
2410
  ]
2118
2411
  }
2119
2412
  },
2413
+ "GetHillChart": {
2414
+ "retry": {
2415
+ "maxAttempts": 3,
2416
+ "baseDelayMs": 1000,
2417
+ "backoff": "exponential",
2418
+ "retryOn": [
2419
+ 429,
2420
+ 503
2421
+ ]
2422
+ }
2423
+ },
2424
+ "UpdateHillChartSettings": {
2425
+ "retry": {
2426
+ "maxAttempts": 3,
2427
+ "baseDelayMs": 1000,
2428
+ "backoff": "exponential",
2429
+ "retryOn": [
2430
+ 429,
2431
+ 503
2432
+ ]
2433
+ },
2434
+ "idempotent": {
2435
+ "natural": true
2436
+ }
2437
+ },
2120
2438
  "ListTodolists": {
2121
2439
  "retry": {
2122
2440
  "maxAttempts": 3,
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for Account operations
6
+ #
7
+ # @generated from OpenAPI spec
8
+ class AccountService < BaseService
9
+
10
+ # Get the account for the current access token
11
+ # @return [Hash] response data
12
+ def get_account()
13
+ with_operation(service: "account", operation: "get_account", is_mutation: false) do
14
+ http_get("/account.json").json
15
+ end
16
+ end
17
+
18
+ # Upload or replace the account logo.
19
+ # @param io [IO, String] File data to upload (IO object or string)
20
+ # @param filename [String] Display name for the uploaded file
21
+ # @param content_type [String] MIME type of the file (e.g., "image/png")
22
+ # @return [void]
23
+ def update_account_logo(io:, filename:, content_type:)
24
+ with_operation(service: "account", operation: "update_account_logo", is_mutation: true) do
25
+ http_put_multipart("/account/logo.json", io: io, filename: filename, content_type: content_type, field: "logo")
26
+ nil
27
+ end
28
+ end
29
+
30
+ # Remove the account logo. Only administrators and account owners can use this endpoint.
31
+ # @return [void]
32
+ def remove_account_logo()
33
+ with_operation(service: "account", operation: "remove_account_logo", is_mutation: true) do
34
+ http_delete("/account/logo.json")
35
+ nil
36
+ end
37
+ end
38
+
39
+ # Rename the current account. Only account owners can use this endpoint.
40
+ # @param name [String] name
41
+ # @return [Hash] response data
42
+ def update_account_name(name:)
43
+ with_operation(service: "account", operation: "update_account_name", is_mutation: true) do
44
+ http_put("/account/name.json", body: compact_params(name: name)).json
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cgi/escape"
4
+ require "securerandom"
4
5
 
5
6
  module Basecamp
6
7
  module Services
@@ -162,12 +163,43 @@ module Basecamp
162
163
  # @see AccountClient#post_raw
163
164
  # @!method paginate(path, params: {}, &block)
164
165
  # @see AccountClient#paginate
165
- %i[get post put delete post_raw].each do |method|
166
+ %i[get post put delete post_raw put_raw].each do |method|
166
167
  define_method(:"http_#{method}") do |*args, **kwargs, &block|
167
168
  @client.public_send(method, *args, **kwargs, &block)
168
169
  end
169
170
  end
170
171
 
172
+ # Upload a file as multipart/form-data.
173
+ # @param path [String] API path
174
+ # @param io [IO, String] file data
175
+ # @param filename [String] display filename
176
+ # @param content_type [String] MIME type
177
+ # @param field [String] form field name
178
+ def http_put_multipart(path, io:, filename:, content_type:, field: "file")
179
+ boundary = "BasecampSDK#{SecureRandom.hex(16)}"
180
+ body = build_multipart_body(boundary: boundary, field: field, io: io, \
181
+ filename: filename, content_type: content_type)
182
+ http_put_raw(path, body: body, content_type: "multipart/form-data; boundary=#{boundary}")
183
+ nil
184
+ end
185
+
186
+ private
187
+
188
+ def build_multipart_body(boundary:, field:, io:, filename:, content_type:)
189
+ data = io.respond_to?(:read) ? io.read : io.to_s
190
+ safe_filename = filename.tr("\r\n", "").gsub("\\", "\\\\").gsub('"', '\\"')
191
+ safe_content_type = content_type.tr("\r\n", "")
192
+ body = "".b
193
+ body << "--#{boundary}\r\n"
194
+ body << "Content-Disposition: form-data; name=\"#{field}\"; filename=\"#{safe_filename}\"\r\n"
195
+ body << "Content-Type: #{safe_content_type}\r\n"
196
+ body << "\r\n"
197
+ body << data
198
+ body << "\r\n"
199
+ body << "--#{boundary}--\r\n"
200
+ body.force_encoding(Encoding::BINARY)
201
+ end
202
+
171
203
  # Paginate doesn't conflict with service methods, keep as-is
172
204
  def paginate(...)
173
205
  @client.paginate(...)
@@ -81,10 +81,13 @@ module Basecamp
81
81
 
82
82
  # List all lines (messages) in a campfire
83
83
  # @param campfire_id [Integer] campfire id ID
84
+ # @param sort [String, nil] created_at|updated_at
85
+ # @param direction [String, nil] asc|desc
84
86
  # @return [Enumerator<Hash>] paginated results
85
- def list_lines(campfire_id:)
87
+ def list_lines(campfire_id:, sort: nil, direction: nil)
86
88
  wrap_paginated(service: "campfires", operation: "list_lines", is_mutation: false, resource_id: campfire_id) do
87
- paginate("/chats/#{campfire_id}/lines.json")
89
+ params = compact_params(sort: sort, direction: direction)
90
+ paginate("/chats/#{campfire_id}/lines.json", params: params)
88
91
  end
89
92
  end
90
93
 
@@ -122,10 +125,13 @@ module Basecamp
122
125
 
123
126
  # List uploaded files in a campfire
124
127
  # @param campfire_id [Integer] campfire id ID
128
+ # @param sort [String, nil] created_at|updated_at
129
+ # @param direction [String, nil] asc|desc
125
130
  # @return [Enumerator<Hash>] paginated results
126
- def list_uploads(campfire_id:)
131
+ def list_uploads(campfire_id:, sort: nil, direction: nil)
127
132
  wrap_paginated(service: "campfires", operation: "list_uploads", is_mutation: false, resource_id: campfire_id) do
128
- paginate("/chats/#{campfire_id}/uploads.json")
133
+ params = compact_params(sort: sort, direction: direction)
134
+ paginate("/chats/#{campfire_id}/uploads.json", params: params)
129
135
  end
130
136
  end
131
137
 
@@ -32,10 +32,11 @@ module Basecamp
32
32
  # Move a card to a different column
33
33
  # @param card_id [Integer] card id ID
34
34
  # @param column_id [Integer] column id
35
+ # @param position [Integer, nil] 1-indexed position within the destination column. Defaults to 1 (top).
35
36
  # @return [void]
36
- def move(card_id:, column_id:)
37
+ def move(card_id:, column_id:, position: nil)
37
38
  with_operation(service: "cards", operation: "move", is_mutation: true, resource_id: card_id) do
38
- http_post("/card_tables/cards/#{card_id}/moves.json", body: compact_params(column_id: column_id))
39
+ http_post("/card_tables/cards/#{card_id}/moves.json", body: compact_params(column_id: column_id, position: position))
39
40
  nil
40
41
  end
41
42
  end
@@ -27,10 +27,11 @@ module Basecamp
27
27
  # Update an existing answer
28
28
  # @param answer_id [Integer] answer id ID
29
29
  # @param content [String] content
30
+ # @param group_on [String, nil] group on (YYYY-MM-DD)
30
31
  # @return [void]
31
- def update_answer(answer_id:, content:)
32
+ def update_answer(answer_id:, content:, group_on: nil)
32
33
  with_operation(service: "checkins", operation: "update_answer", is_mutation: true, resource_id: answer_id) do
33
- http_put("/question_answers/#{answer_id}", body: compact_params(content: content))
34
+ http_put("/question_answers/#{answer_id}", body: compact_params(content: content, group_on: group_on))
34
35
  nil
35
36
  end
36
37
  end
@@ -8,10 +8,13 @@ module Basecamp
8
8
  class ClientApprovalsService < BaseService
9
9
 
10
10
  # List all client approvals in a project
11
+ # @param sort [String, nil] created_at|updated_at
12
+ # @param direction [String, nil] asc|desc
11
13
  # @return [Enumerator<Hash>] paginated results
12
- def list()
14
+ def list(sort: nil, direction: nil)
13
15
  wrap_paginated(service: "clientapprovals", operation: "list", is_mutation: false) do
14
- paginate("/client/approvals.json")
16
+ params = compact_params(sort: sort, direction: direction)
17
+ paginate("/client/approvals.json", params: params)
15
18
  end
16
19
  end
17
20
 
@@ -8,10 +8,13 @@ module Basecamp
8
8
  class ClientCorrespondencesService < BaseService
9
9
 
10
10
  # List all client correspondences in a project
11
+ # @param sort [String, nil] created_at|updated_at
12
+ # @param direction [String, nil] asc|desc
11
13
  # @return [Enumerator<Hash>] paginated results
12
- def list()
14
+ def list(sort: nil, direction: nil)
13
15
  wrap_paginated(service: "clientcorrespondences", operation: "list", is_mutation: false) do
14
- paginate("/client/correspondences.json")
16
+ params = compact_params(sort: sort, direction: direction)
17
+ paginate("/client/correspondences.json", params: params)
15
18
  end
16
19
  end
17
20
 
@@ -56,10 +56,13 @@ module Basecamp
56
56
 
57
57
  # List all forwards in an inbox
58
58
  # @param inbox_id [Integer] inbox id ID
59
+ # @param sort [String, nil] created_at|updated_at
60
+ # @param direction [String, nil] asc|desc
59
61
  # @return [Enumerator<Hash>] paginated results
60
- def list(inbox_id:)
62
+ def list(inbox_id:, sort: nil, direction: nil)
61
63
  wrap_paginated(service: "forwards", operation: "list", is_mutation: false, resource_id: inbox_id) do
62
- paginate("/inboxes/#{inbox_id}/forwards.json")
64
+ params = compact_params(sort: sort, direction: direction)
65
+ paginate("/inboxes/#{inbox_id}/forwards.json", params: params)
63
66
  end
64
67
  end
65
68
  end