basecamp-sdk 0.19.0 → 0.21.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: c53198686c7bf2b7f9fc25f60c14dec77dada2f3bbf7b6070f9a3e0639f7b529
4
- data.tar.gz: f25233709c5a99476ed38728cf9ed005eb55e39457cc6dd4ea3250ac72583e60
3
+ metadata.gz: 7b16ed47ffa033dcc0c642c37ebc51a4bc6552fa06a088fd268cb451fa09e2f8
4
+ data.tar.gz: 48c691a48f55cc68f9d3d5208488c8baecb56264a230614a22e6b7746d8a7451
5
5
  SHA512:
6
- metadata.gz: 3d17039b8cddadbbc8ee270f46f0f3e4665651091c0cd0371728693398002dfdc9def64bc081316c496ca477e43a1e2b4e55b65bc53a2d47124694989d5a0437
7
- data.tar.gz: 9bf77edeae08a13c60d1f25f3e9d16ad9d39298dffa7cc430fa319aee4ecfa208d62c1c3ccec629473058ac9b48569faea8190b4373313923caa48556a1ab83a
6
+ metadata.gz: 20f3979bfb268ce78303e4b10aadb50194a4b20818876a076753560260f54d70a9cbd42851a7a4337cdefc316a1800f6d4088437cd68994a684b2e9f60b2d016
7
+ data.tar.gz: 88bb6d1ec5889506b6f24660ce919befa780057f37587143526d524286d6fcd1d098aed95971f136400cfc1ab65656f28ea9f605c24ac543c5f9f0c02474fd74
data/README.md CHANGED
@@ -347,6 +347,7 @@ The SDK provides the full account-scoped service set documented in SPEC §5. The
347
347
  | `card_tables` | Card tables (kanban) |
348
348
  | `card_columns` | Card table columns |
349
349
  | `card_steps` | Card workflow steps |
350
+ | `subtasks` | Subtasks (checklist items) on to-dos and cards |
350
351
  | `wormholes` | Card table wormholes (cross-project moves) |
351
352
  | `lineup` | Card lineup view |
352
353
  | `tools` | Project dock tools |
@@ -516,11 +516,6 @@ module Basecamp
516
516
  service(:lineup) { Services::LineupService.new(self) }
517
517
  end
518
518
 
519
- # @return [Services::AutomationService]
520
- def automation
521
- service(:automation) { Services::AutomationService.new(self) }
522
- end
523
-
524
519
  # @return [Services::MessageTypesService]
525
520
  def message_types
526
521
  service(:message_types) { Services::MessageTypesService.new(self) }
@@ -612,6 +607,11 @@ module Basecamp
612
607
  service(:boosts) { Services::BoostsService.new(self) }
613
608
  end
614
609
 
610
+ # @return [Services::SubtasksService]
611
+ def subtasks
612
+ service(:subtasks) { Services::SubtasksService.new(self) }
613
+ end
614
+
615
615
  # @return [Services::AccountService]
616
616
  def account
617
617
  service(:account) { Services::AccountService.new(self) }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://basecamp.com/schemas/sdk-metadata.json",
3
3
  "version": "1.0.0",
4
- "generated": "2026-09-16T14:38:22Z",
4
+ "generated": "2026-09-30T18:52:50Z",
5
5
  "operations": {
6
6
  "GetAccount": {
7
7
  "retry": {
@@ -375,6 +375,28 @@
375
375
  ]
376
376
  }
377
377
  },
378
+ "CreateTemplatification": {
379
+ "retry": {
380
+ "maxAttempts": 2,
381
+ "baseDelayMs": 1000,
382
+ "backoff": "exponential",
383
+ "retryOn": [
384
+ 429,
385
+ 503
386
+ ]
387
+ }
388
+ },
389
+ "GetTemplatification": {
390
+ "retry": {
391
+ "maxAttempts": 3,
392
+ "baseDelayMs": 1000,
393
+ "backoff": "exponential",
394
+ "retryOn": [
395
+ 429,
396
+ 503
397
+ ]
398
+ }
399
+ },
378
400
  "CreateTodosetTodo": {
379
401
  "retry": {
380
402
  "maxAttempts": 3,
@@ -2489,6 +2511,33 @@
2489
2511
  "natural": true
2490
2512
  }
2491
2513
  },
2514
+ "ListSubtasks": {
2515
+ "retry": {
2516
+ "maxAttempts": 3,
2517
+ "baseDelayMs": 1000,
2518
+ "backoff": "exponential",
2519
+ "retryOn": [
2520
+ 429,
2521
+ 503
2522
+ ]
2523
+ },
2524
+ "pagination": {
2525
+ "style": "link",
2526
+ "totalCountHeader": "X-Total-Count",
2527
+ "maxPageSize": 50
2528
+ }
2529
+ },
2530
+ "CreateSubtask": {
2531
+ "retry": {
2532
+ "maxAttempts": 2,
2533
+ "baseDelayMs": 1000,
2534
+ "backoff": "exponential",
2535
+ "retryOn": [
2536
+ 429,
2537
+ 503
2538
+ ]
2539
+ }
2540
+ },
2492
2541
  "GetRecordingTimesheet": {
2493
2542
  "retry": {
2494
2543
  "maxAttempts": 3,
@@ -2834,7 +2883,7 @@
2834
2883
  "natural": true
2835
2884
  }
2836
2885
  },
2837
- "GetTemplateLibrary": {
2886
+ "GetSubtask": {
2838
2887
  "retry": {
2839
2888
  "maxAttempts": 3,
2840
2889
  "baseDelayMs": 1000,
@@ -2845,6 +2894,98 @@
2845
2894
  ]
2846
2895
  }
2847
2896
  },
2897
+ "UpdateSubtask": {
2898
+ "retry": {
2899
+ "maxAttempts": 3,
2900
+ "baseDelayMs": 1000,
2901
+ "backoff": "exponential",
2902
+ "retryOn": [
2903
+ 429,
2904
+ 503
2905
+ ]
2906
+ },
2907
+ "idempotent": {
2908
+ "natural": true
2909
+ }
2910
+ },
2911
+ "DeleteSubtask": {
2912
+ "retry": {
2913
+ "maxAttempts": 3,
2914
+ "baseDelayMs": 1000,
2915
+ "backoff": "exponential",
2916
+ "retryOn": [
2917
+ 429,
2918
+ 503
2919
+ ]
2920
+ },
2921
+ "idempotent": {
2922
+ "natural": true
2923
+ }
2924
+ },
2925
+ "CompleteSubtask": {
2926
+ "retry": {
2927
+ "maxAttempts": 3,
2928
+ "baseDelayMs": 1000,
2929
+ "backoff": "exponential",
2930
+ "retryOn": [
2931
+ 429,
2932
+ 503
2933
+ ]
2934
+ },
2935
+ "idempotent": {
2936
+ "natural": true
2937
+ }
2938
+ },
2939
+ "UncompleteSubtask": {
2940
+ "retry": {
2941
+ "maxAttempts": 3,
2942
+ "baseDelayMs": 1000,
2943
+ "backoff": "exponential",
2944
+ "retryOn": [
2945
+ 429,
2946
+ 503
2947
+ ]
2948
+ },
2949
+ "idempotent": {
2950
+ "natural": true
2951
+ }
2952
+ },
2953
+ "RepositionSubtask": {
2954
+ "retry": {
2955
+ "maxAttempts": 3,
2956
+ "baseDelayMs": 1000,
2957
+ "backoff": "exponential",
2958
+ "retryOn": [
2959
+ 429,
2960
+ 503
2961
+ ]
2962
+ },
2963
+ "idempotent": {
2964
+ "natural": true
2965
+ }
2966
+ },
2967
+ "GetTemplateLibraryCardTables": {
2968
+ "retry": {
2969
+ "maxAttempts": 3,
2970
+ "baseDelayMs": 1000,
2971
+ "backoff": "exponential",
2972
+ "retryOn": [
2973
+ 429,
2974
+ 503
2975
+ ]
2976
+ }
2977
+ },
2978
+ "CreateTemplateLibraryCardTable": {
2979
+ "retry": {
2980
+ "maxAttempts": 2,
2981
+ "baseDelayMs": 1000,
2982
+ "backoff": "exponential",
2983
+ "retryOn": [
2984
+ 429,
2985
+ 503
2986
+ ]
2987
+ }
2988
+ },
2848
2989
  "CreateTemplateLibraryCopy": {
2849
2990
  "retry": {
2850
2991
  "maxAttempts": 2,
@@ -2867,6 +3008,28 @@
2867
3008
  ]
2868
3009
  }
2869
3010
  },
3011
+ "GetTemplateLibraryTodolists": {
3012
+ "retry": {
3013
+ "maxAttempts": 3,
3014
+ "baseDelayMs": 1000,
3015
+ "backoff": "exponential",
3016
+ "retryOn": [
3017
+ 429,
3018
+ 503
3019
+ ]
3020
+ }
3021
+ },
3022
+ "CreateTemplateLibraryTodolist": {
3023
+ "retry": {
3024
+ "maxAttempts": 2,
3025
+ "baseDelayMs": 1000,
3026
+ "backoff": "exponential",
3027
+ "retryOn": [
3028
+ 429,
3029
+ 503
3030
+ ]
3031
+ }
3032
+ },
2870
3033
  "ListTemplates": {
2871
3034
  "retry": {
2872
3035
  "maxAttempts": 3,
@@ -3566,7 +3729,35 @@
3566
3729
  "creator",
3567
3730
  "participants.[]"
3568
3731
  ],
3732
+ "CreateSubtask": [
3733
+ "assignees.[]",
3734
+ "completer",
3735
+ "creator"
3736
+ ],
3737
+ "CreateTemplateLibraryCardTable": [
3738
+ "creator",
3739
+ "lists.[].creator",
3740
+ "lists.[].subscribers.[]",
3741
+ "subscribers.[]",
3742
+ "wormholes.[].creator"
3743
+ ],
3569
3744
  "CreateTemplateLibraryCopy": [
3745
+ "destination_card_table.creator",
3746
+ "destination_card_table.lists.[].creator",
3747
+ "destination_card_table.lists.[].subscribers.[]",
3748
+ "destination_card_table.subscribers.[]",
3749
+ "destination_card_table.wormholes.[].creator",
3750
+ "destination_todolist.creator"
3751
+ ],
3752
+ "CreateTemplateLibraryTodolist": [
3753
+ "creator"
3754
+ ],
3755
+ "CreateTemplatification": [
3756
+ "destination_card_table.creator",
3757
+ "destination_card_table.lists.[].creator",
3758
+ "destination_card_table.lists.[].subscribers.[]",
3759
+ "destination_card_table.subscribers.[]",
3760
+ "destination_card_table.wormholes.[].creator",
3570
3761
  "destination_todolist.creator"
3571
3762
  ],
3572
3763
  "CreateTimesheetEntry": [
@@ -3914,10 +4105,31 @@
3914
4105
  "GetSubscription": [
3915
4106
  "subscribers.[]"
3916
4107
  ],
3917
- "GetTemplateLibrary": [
3918
- "todolists.[].creator"
4108
+ "GetSubtask": [
4109
+ "assignees.[]",
4110
+ "completer",
4111
+ "creator"
4112
+ ],
4113
+ "GetTemplateLibraryCardTables": [
4114
+ "card_tables.[].creator"
3919
4115
  ],
3920
4116
  "GetTemplateLibraryCopy": [
4117
+ "destination_card_table.creator",
4118
+ "destination_card_table.lists.[].creator",
4119
+ "destination_card_table.lists.[].subscribers.[]",
4120
+ "destination_card_table.subscribers.[]",
4121
+ "destination_card_table.wormholes.[].creator",
4122
+ "destination_todolist.creator"
4123
+ ],
4124
+ "GetTemplateLibraryTodolists": [
4125
+ "todolists.[].creator"
4126
+ ],
4127
+ "GetTemplatification": [
4128
+ "destination_card_table.creator",
4129
+ "destination_card_table.lists.[].creator",
4130
+ "destination_card_table.lists.[].subscribers.[]",
4131
+ "destination_card_table.subscribers.[]",
4132
+ "destination_card_table.wormholes.[].creator",
3921
4133
  "destination_todolist.creator"
3922
4134
  ],
3923
4135
  "GetTimesheetEntry": [
@@ -4047,6 +4259,11 @@
4047
4259
  "[].creator",
4048
4260
  "[].participants.[]"
4049
4261
  ],
4262
+ "ListSubtasks": [
4263
+ "[].assignees.[]",
4264
+ "[].completer",
4265
+ "[].creator"
4266
+ ],
4050
4267
  "ListTodolistGroups": [
4051
4268
  "[].creator"
4052
4269
  ],
@@ -4162,6 +4379,11 @@
4162
4379
  "UpdateSubscription": [
4163
4380
  "subscribers.[]"
4164
4381
  ],
4382
+ "UpdateSubtask": [
4383
+ "assignees.[]",
4384
+ "completer",
4385
+ "creator"
4386
+ ],
4165
4387
  "UpdateTimesheetEntry": [
4166
4388
  "creator",
4167
4389
  "person"
@@ -10,7 +10,9 @@ module Basecamp
10
10
  # Reposition a step within a card
11
11
  # @param card_id [Integer] card id ID
12
12
  # @param source_id [Integer] source id
13
- # @param position [Integer] 0-indexed position
13
+ # @param position [Integer] The 1-based position to move it to (1 = top), the same `reposition_to`
14
+ # a to-do uses. bc3's doc said "Zero indexed" until BC3 #12659 corrected
15
+ # it; the server never was.
14
16
  # @return [void]
15
17
  def reposition(card_id:, source_id:, position:)
16
18
  with_operation(service: "cardsteps", operation: "reposition", is_mutation: true, resource_id: card_id) do
@@ -7,6 +7,14 @@ module Basecamp
7
7
  # @generated from OpenAPI spec
8
8
  class LineupService < BaseService
9
9
 
10
+ # List all lineup markers for the account
11
+ # @return [Array<Hash>] response data
12
+ def list_lineup_markers()
13
+ with_operation(service: "lineup", operation: "list_lineup_markers", is_mutation: false) do
14
+ http_get("/lineup/markers.json", operation: "ListLineupMarkers").json(operation: "ListLineupMarkers")
15
+ end
16
+ end
17
+
10
18
  # Create a new lineup marker
11
19
  # @param name [String] name
12
20
  # @param date [String] date
@@ -34,7 +34,8 @@ module Basecamp
34
34
  end
35
35
 
36
36
  # Mark specified items as read
37
- # @param readables [Array] Array of readable_sgid values identifying the items to mark as read
37
+ # @param readables [Array] Array of readable_sgid values identifying the items to mark as read.
38
+ # At most 500 per request.
38
39
  # @return [void]
39
40
  def mark_as_read(readables:)
40
41
  with_operation(service: "mynotifications", operation: "mark_as_read", is_mutation: true) do
@@ -52,7 +52,7 @@ module Basecamp
52
52
  end
53
53
  end
54
54
 
55
- # Archive a recording
55
+ # Archive a recording; bc3 answers 403 for types it never lets be archived, timesheet entries among them
56
56
  # @param recording_id [Integer] recording id ID
57
57
  # @return [void]
58
58
  def archive(recording_id:)
@@ -62,7 +62,7 @@ module Basecamp
62
62
  end
63
63
  end
64
64
 
65
- # Trash a recording
65
+ # Trash a recording; bc3 answers 403 for types it never lets be trashed, timesheet entries among them (DestroyTimesheetEntry removes those, permanently)
66
66
  # @param recording_id [Integer] recording id ID
67
67
  # @return [void]
68
68
  def trash(recording_id:)
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecamp
4
+ module Services
5
+ # Service for Subtasks operations
6
+ #
7
+ # @generated from OpenAPI spec
8
+ class SubtasksService < BaseService
9
+
10
+ # List a recording's subtasks, in position order
11
+ # @param recording_id [Integer] recording id ID
12
+ # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
13
+ # @param max_items [Integer, nil] cap on items yielded across pages; nil or non-positive means no cap
14
+ # @return [ListEnumerator<Hash>] lazily paginated results (#meta carries pagination metadata)
15
+ def list(recording_id:, page: nil, max_items: nil)
16
+ wrap_paginated(service: "subtasks", operation: "list", is_mutation: false, resource_id: recording_id) do
17
+ params = compact_query_params(page: page)
18
+ paginate("/recordings/#{recording_id}/subtasks.json", params: params, operation: "ListSubtasks", max_items: max_items)
19
+ end
20
+ end
21
+
22
+ # Create a subtask under a to-do or a card
23
+ # @param recording_id [Integer] recording id ID
24
+ # @param title [String] title
25
+ # @param due_on [String, nil] due on (YYYY-MM-DD)
26
+ # @param assignee_ids [Array, nil] assignee ids
27
+ # @return [Hash] response data
28
+ def create(recording_id:, title:, due_on: nil, assignee_ids: nil)
29
+ with_operation(service: "subtasks", operation: "create", is_mutation: true, resource_id: recording_id) do
30
+ http_post("/recordings/#{recording_id}/subtasks.json", body: compact_params(title: title, due_on: due_on, assignee_ids: assignee_ids)).json(operation: "CreateSubtask")
31
+ end
32
+ end
33
+
34
+ # Get a subtask by ID
35
+ # @param subtask_id [Integer] subtask id ID
36
+ # @return [Hash] response data
37
+ def get(subtask_id:)
38
+ with_operation(service: "subtasks", operation: "get", is_mutation: false, resource_id: subtask_id) do
39
+ http_get("/subtasks/#{subtask_id}", operation: "GetSubtask").json(operation: "GetSubtask")
40
+ end
41
+ end
42
+
43
+ # Update a subtask
44
+ # @param subtask_id [Integer] subtask id ID
45
+ # @param title [String, nil] title
46
+ # @param due_on [String, nil] due on (YYYY-MM-DD)
47
+ # @param assignee_ids [Array, nil] assignee ids
48
+ # @return [Hash] response data
49
+ def update(subtask_id:, title: nil, due_on: nil, assignee_ids: nil)
50
+ with_operation(service: "subtasks", operation: "update", is_mutation: true, resource_id: subtask_id) do
51
+ http_put("/subtasks/#{subtask_id}", body: compact_params(title: title, due_on: due_on, assignee_ids: assignee_ids)).json(operation: "UpdateSubtask")
52
+ end
53
+ end
54
+
55
+ # Delete a subtask
56
+ # @param subtask_id [Integer] subtask id ID
57
+ # @return [void]
58
+ def delete(subtask_id:)
59
+ with_operation(service: "subtasks", operation: "delete", is_mutation: true, resource_id: subtask_id) do
60
+ http_delete("/subtasks/#{subtask_id}")
61
+ nil
62
+ end
63
+ end
64
+
65
+ # Mark a subtask as completed
66
+ # @param subtask_id [Integer] subtask id ID
67
+ # @return [void]
68
+ def complete(subtask_id:)
69
+ with_operation(service: "subtasks", operation: "complete", is_mutation: true, resource_id: subtask_id) do
70
+ http_post("/subtasks/#{subtask_id}/completion.json")
71
+ nil
72
+ end
73
+ end
74
+
75
+ # Mark a subtask as not completed
76
+ # @param subtask_id [Integer] subtask id ID
77
+ # @return [void]
78
+ def uncomplete(subtask_id:)
79
+ with_operation(service: "subtasks", operation: "uncomplete", is_mutation: true, resource_id: subtask_id) do
80
+ http_delete("/subtasks/#{subtask_id}/completion.json")
81
+ nil
82
+ end
83
+ end
84
+
85
+ # Move a subtask to a new position among its siblings
86
+ # @param subtask_id [Integer] subtask id ID
87
+ # @param position [Integer] The 1-based position to move it to
88
+ # @return [void]
89
+ def reposition(subtask_id:, position:)
90
+ with_operation(service: "subtasks", operation: "reposition", is_mutation: true, resource_id: subtask_id) do
91
+ http_put("/subtasks/#{subtask_id}/position.json", body: compact_params(position: position))
92
+ nil
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -7,22 +7,63 @@ module Basecamp
7
7
  # @generated from OpenAPI spec
8
8
  class TemplatesService < BaseService
9
9
 
10
- # Get the account's to-do list template library
10
+ # Templatify a to-do list or card table
11
+ # @param bucket_id [Integer] bucket id ID
12
+ # @param recording_id [Integer] The to-do list or card table to templatify. Anything else is a 403.
13
+ # @param template_name [String, nil] What to call the template. Defaults to the source recording's own title.
14
+ # @param copy_comments [Boolean, nil] Carry the comments across.
15
+ # @param copy_assignments [Boolean, nil] Carry assignees and the people involved across, adding them to the library
16
+ # if they are not already there.
17
+ # @param move_cards_to_triage [Boolean, nil] Gather the cards into Triage. Card tables only, and ignored otherwise.
11
18
  # @return [Hash] response data
12
- def get_library()
13
- with_operation(service: "templates", operation: "get_library", is_mutation: false) do
14
- http_get("/template_library.json", operation: "GetTemplateLibrary").json(operation: "GetTemplateLibrary")
19
+ def create_templatification(bucket_id:, recording_id:, template_name: nil, copy_comments: nil, copy_assignments: nil, move_cards_to_triage: nil)
20
+ with_operation(service: "templates", operation: "create_templatification", is_mutation: true, project_id: bucket_id, resource_id: recording_id) do
21
+ http_post("/buckets/#{bucket_id}/recordings/#{recording_id}/templatifications.json", body: compact_params(template_name: template_name, copy_comments: copy_comments, copy_assignments: copy_assignments, move_cards_to_triage: move_cards_to_triage)).json(operation: "CreateTemplatification")
15
22
  end
16
23
  end
17
24
 
18
- # Start copying a to-do list template into a project
19
- # @param template_recording_id [Integer] template recording id
20
- # @param destination_parent_id [Integer] destination parent id
25
+ # Get a templatification
26
+ # @param bucket_id [Integer] bucket id ID
27
+ # @param recording_id [Integer] recording id ID
28
+ # @param templatification_id [Integer] templatification id ID
29
+ # @return [Hash] response data
30
+ def get_templatification(bucket_id:, recording_id:, templatification_id:)
31
+ with_operation(service: "templates", operation: "get_templatification", is_mutation: false, project_id: bucket_id, resource_id: templatification_id) do
32
+ http_get("/buckets/#{bucket_id}/recordings/#{recording_id}/templatifications/#{templatification_id}", operation: "GetTemplatification").json(operation: "GetTemplatification")
33
+ end
34
+ end
35
+
36
+ # Get the account's card table templates
37
+ # @return [Hash] response data
38
+ def get_library_card_tables()
39
+ with_operation(service: "templates", operation: "get_library_card_tables", is_mutation: false) do
40
+ http_get("/template_library/card_tables.json", operation: "GetTemplateLibraryCardTables").json(operation: "GetTemplateLibraryCardTables")
41
+ end
42
+ end
43
+
44
+ # Create a card table template with the default columns
45
+ # @param name [String] The template's name. Write-only: the response carries it as `title`.
46
+ # @return [Hash] response data
47
+ def create_library_card_table(name:)
48
+ with_operation(service: "templates", operation: "create_library_card_table", is_mutation: true) do
49
+ http_post("/template_library/card_tables.json", body: compact_params(name: name)).json(operation: "CreateTemplateLibraryCardTable")
50
+ end
51
+ end
52
+
53
+ # Start copying a to-do list or card table template into a project
54
+ # @param template_recording_id [Integer] The to-do list or card table in the library to copy.
55
+ # @param destination_project_id [Integer, nil] The destination project. Basecamp resolves the container from the
56
+ # template's kind, so a caller naming a project needs to know nothing about
57
+ # docks or to-do sets. Supply this or destination_parent_id; if both are
58
+ # sent, destination_parent_id wins.
59
+ # @param destination_parent_id [Integer, nil] The container to copy into, for a caller that already holds one: the
60
+ # project's to-do set for a to-do list template, or its dock for a card
61
+ # table. A caller who may not edit it gets 404, not 403.
21
62
  # @param adding_people_confirmed [Boolean, nil] Confirm granting destination-project access to people referenced by the template.
22
63
  # @return [Hash] response data
23
- def create_library_copy(template_recording_id:, destination_parent_id:, adding_people_confirmed: nil)
64
+ def create_library_copy(template_recording_id:, destination_project_id: nil, destination_parent_id: nil, adding_people_confirmed: nil)
24
65
  with_operation(service: "templates", operation: "create_library_copy", is_mutation: true) do
25
- http_post("/template_library/copies.json", body: compact_params(template_recording_id: template_recording_id, destination_parent_id: destination_parent_id, adding_people_confirmed: adding_people_confirmed)).json(operation: "CreateTemplateLibraryCopy")
66
+ http_post("/template_library/copies.json", body: compact_params(template_recording_id: template_recording_id, destination_project_id: destination_project_id, destination_parent_id: destination_parent_id, adding_people_confirmed: adding_people_confirmed)).json(operation: "CreateTemplateLibraryCopy")
26
67
  end
27
68
  end
28
69
 
@@ -35,6 +76,24 @@ module Basecamp
35
76
  end
36
77
  end
37
78
 
79
+ # Get the account's to-do list templates
80
+ # @return [Hash] response data
81
+ def get_library_todolists()
82
+ with_operation(service: "templates", operation: "get_library_todolists", is_mutation: false) do
83
+ http_get("/template_library/todolists.json", operation: "GetTemplateLibraryTodolists").json(operation: "GetTemplateLibraryTodolists")
84
+ end
85
+ end
86
+
87
+ # Create an empty to-do list template
88
+ # @param name [String] What to call the template.
89
+ # @param description [String, nil] Rich text describing the template.
90
+ # @return [Hash] response data
91
+ def create_library_todolist(name:, description: nil)
92
+ with_operation(service: "templates", operation: "create_library_todolist", is_mutation: true) do
93
+ http_post("/template_library/todolists.json", body: compact_params(name: name, description: description)).json(operation: "CreateTemplateLibraryTodolist")
94
+ end
95
+ end
96
+
38
97
  # List all templates visible to the current user
39
98
  # @param status [String, nil] active|archived|trashed
40
99
  # @param page [Integer, nil] Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.
@@ -83,7 +83,7 @@ module Basecamp
83
83
  end
84
84
  end
85
85
 
86
- # Permanently delete a timesheet entry; answers 403 when the caller may not archive or trash it.
86
+ # Permanently delete a timesheet entry, which never appears in the trash and cannot be restored; this is the only way to remove one, since bc3 answers 403 to TrashRecording and ArchiveRecording for every timesheet entry, and it answers 403 when the caller may not remove the entry.
87
87
  # @param entry_id [Integer] entry id ID
88
88
  # @return [void]
89
89
  def destroy(entry_id:)