@37signals/basecamp 0.14.0 → 0.16.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.
Files changed (96) hide show
  1. package/README.md +1 -1
  2. package/dist/client.d.ts +5 -2
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +4 -2
  5. package/dist/client.js.map +1 -1
  6. package/dist/download.d.ts +3 -1
  7. package/dist/download.d.ts.map +1 -1
  8. package/dist/download.js +87 -23
  9. package/dist/download.js.map +1 -1
  10. package/dist/errors.d.ts +40 -0
  11. package/dist/errors.d.ts.map +1 -1
  12. package/dist/errors.js +132 -15
  13. package/dist/errors.js.map +1 -1
  14. package/dist/generated/metadata.d.ts.map +1 -1
  15. package/dist/generated/metadata.js +115 -1
  16. package/dist/generated/metadata.js.map +1 -1
  17. package/dist/generated/metadata.ts +115 -1
  18. package/dist/generated/openapi-stripped.json +1412 -209
  19. package/dist/generated/path-mapping.d.ts.map +1 -1
  20. package/dist/generated/path-mapping.js +9 -0
  21. package/dist/generated/path-mapping.js.map +1 -1
  22. package/dist/generated/path-mapping.ts +9 -0
  23. package/dist/generated/schema.d.ts +1136 -85
  24. package/dist/generated/services/bubble-ups.d.ts +48 -0
  25. package/dist/generated/services/bubble-ups.d.ts.map +1 -0
  26. package/dist/generated/services/bubble-ups.js +67 -0
  27. package/dist/generated/services/bubble-ups.js.map +1 -0
  28. package/dist/generated/services/bubble-ups.ts +98 -0
  29. package/dist/generated/services/checkins.d.ts +1 -1
  30. package/dist/generated/services/checkins.d.ts.map +1 -1
  31. package/dist/generated/services/checkins.ts +1 -1
  32. package/dist/generated/services/gauges.d.ts +3 -2
  33. package/dist/generated/services/gauges.d.ts.map +1 -1
  34. package/dist/generated/services/gauges.ts +2 -2
  35. package/dist/generated/services/index.d.ts +1 -0
  36. package/dist/generated/services/index.d.ts.map +1 -1
  37. package/dist/generated/services/index.js +1 -0
  38. package/dist/generated/services/index.js.map +1 -1
  39. package/dist/generated/services/index.ts +1 -0
  40. package/dist/generated/services/projects.d.ts +22 -0
  41. package/dist/generated/services/projects.d.ts.map +1 -1
  42. package/dist/generated/services/projects.js +42 -0
  43. package/dist/generated/services/projects.js.map +1 -1
  44. package/dist/generated/services/projects.ts +53 -0
  45. package/dist/generated/services/recordings.d.ts +24 -0
  46. package/dist/generated/services/recordings.d.ts.map +1 -1
  47. package/dist/generated/services/recordings.js +49 -0
  48. package/dist/generated/services/recordings.js.map +1 -1
  49. package/dist/generated/services/recordings.ts +59 -0
  50. package/dist/generated/services/search.d.ts +2 -1
  51. package/dist/generated/services/search.d.ts.map +1 -1
  52. package/dist/generated/services/search.ts +1 -1
  53. package/dist/generated/services/templates.d.ts +46 -0
  54. package/dist/generated/services/templates.d.ts.map +1 -1
  55. package/dist/generated/services/templates.js +70 -0
  56. package/dist/generated/services/templates.js.map +1 -1
  57. package/dist/generated/services/templates.ts +98 -0
  58. package/dist/index.d.ts +3 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +2 -1
  61. package/dist/index.js.map +1 -1
  62. package/dist/oauth/authorization-document.d.ts +18 -11
  63. package/dist/oauth/authorization-document.d.ts.map +1 -1
  64. package/dist/oauth/authorization-document.js +16 -11
  65. package/dist/oauth/authorization-document.js.map +1 -1
  66. package/dist/oauth/device.d.ts.map +1 -1
  67. package/dist/oauth/device.js +9 -71
  68. package/dist/oauth/device.js.map +1 -1
  69. package/dist/oauth/exchange.d.ts.map +1 -1
  70. package/dist/oauth/exchange.js +55 -16
  71. package/dist/oauth/exchange.js.map +1 -1
  72. package/dist/oauth/limits.d.ts +33 -2
  73. package/dist/oauth/limits.d.ts.map +1 -1
  74. package/dist/oauth/limits.js +76 -2
  75. package/dist/oauth/limits.js.map +1 -1
  76. package/dist/retry.d.ts +23 -0
  77. package/dist/retry.d.ts.map +1 -1
  78. package/dist/retry.js +45 -10
  79. package/dist/retry.js.map +1 -1
  80. package/dist/services/base.d.ts +25 -2
  81. package/dist/services/base.d.ts.map +1 -1
  82. package/dist/services/base.js +85 -15
  83. package/dist/services/base.js.map +1 -1
  84. package/package.json +6 -4
  85. package/src/generated/metadata.ts +115 -1
  86. package/src/generated/openapi-stripped.json +1412 -209
  87. package/src/generated/path-mapping.ts +9 -0
  88. package/src/generated/schema.d.ts +1136 -85
  89. package/src/generated/services/bubble-ups.ts +98 -0
  90. package/src/generated/services/checkins.ts +1 -1
  91. package/src/generated/services/gauges.ts +2 -2
  92. package/src/generated/services/index.ts +1 -0
  93. package/src/generated/services/projects.ts +53 -0
  94. package/src/generated/services/recordings.ts +59 -0
  95. package/src/generated/services/search.ts +1 -1
  96. package/src/generated/services/templates.ts +98 -0
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Basecamp",
5
- "version": "2026-08-05",
5
+ "version": "2026-09-02",
6
6
  "description": "Basecamp API",
7
7
  "contact": {
8
8
  "name": "Basecamp",
@@ -11204,6 +11204,77 @@
11204
11204
  }
11205
11205
  }
11206
11206
  },
11207
+ "/my/recent_projects.json": {
11208
+ "get": {
11209
+ "description": "List the projects the current user has most recently visited, most recent visit first.\nReads the per-user visit log — capped at the 50 most recent visits, keeping\nonly active projects the user can still access — not the home grid's\npinned-exclusion and padding. This endpoint is not paginated. Each entry is\nthe standard project projection plus the current user's bookmarked flag. A\nvisit is recorded when the user opens a project in Basecamp, when they\ncreate one, and by RecordProjectVisit.",
11210
+ "operationId": "ListRecentProjects",
11211
+ "parameters": [],
11212
+ "responses": {
11213
+ "200": {
11214
+ "description": "ListRecentProjects 200 response",
11215
+ "content": {
11216
+ "application/json": {
11217
+ "schema": {
11218
+ "$ref": "#/components/schemas/ListRecentProjectsResponseContent"
11219
+ }
11220
+ }
11221
+ }
11222
+ },
11223
+ "401": {
11224
+ "description": "UnauthorizedError 401 response",
11225
+ "content": {
11226
+ "application/json": {
11227
+ "schema": {
11228
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
11229
+ }
11230
+ }
11231
+ }
11232
+ },
11233
+ "403": {
11234
+ "description": "ForbiddenError 403 response",
11235
+ "content": {
11236
+ "application/json": {
11237
+ "schema": {
11238
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
11239
+ }
11240
+ }
11241
+ }
11242
+ },
11243
+ "429": {
11244
+ "description": "RateLimitError 429 response",
11245
+ "content": {
11246
+ "application/json": {
11247
+ "schema": {
11248
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
11249
+ }
11250
+ }
11251
+ }
11252
+ },
11253
+ "500": {
11254
+ "description": "InternalServerError 500 response",
11255
+ "content": {
11256
+ "application/json": {
11257
+ "schema": {
11258
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
11259
+ }
11260
+ }
11261
+ }
11262
+ }
11263
+ },
11264
+ "tags": [
11265
+ "Projects"
11266
+ ],
11267
+ "x-basecamp-retry": {
11268
+ "maxAttempts": 3,
11269
+ "baseDelayMs": 1000,
11270
+ "backoff": "exponential",
11271
+ "retryOn": [
11272
+ 429,
11273
+ 503
11274
+ ]
11275
+ }
11276
+ }
11277
+ },
11207
11278
  "/my/unreads.json": {
11208
11279
  "put": {
11209
11280
  "description": "Mark specified items as read",
@@ -13012,6 +13083,93 @@
13012
13083
  }
13013
13084
  }
13014
13085
  },
13086
+ "/projects/{projectId}/recent_visit.json": {
13087
+ "post": {
13088
+ "description": "Record that the current user visited a project, moving it to the front of ListRecentProjects (returns 204 No Content).\nIdempotent: re-recording a visit refreshes the same entry. Visits to\narchived or trashed projects are accepted but not recorded, and an\ninaccessible project answers 404.",
13089
+ "operationId": "RecordProjectVisit",
13090
+ "parameters": [
13091
+ {
13092
+ "name": "projectId",
13093
+ "in": "path",
13094
+ "schema": {
13095
+ "type": "integer",
13096
+ "format": "int64"
13097
+ },
13098
+ "required": true
13099
+ }
13100
+ ],
13101
+ "responses": {
13102
+ "204": {
13103
+ "description": "RecordProjectVisit 204 response"
13104
+ },
13105
+ "401": {
13106
+ "description": "UnauthorizedError 401 response",
13107
+ "content": {
13108
+ "application/json": {
13109
+ "schema": {
13110
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
13111
+ }
13112
+ }
13113
+ }
13114
+ },
13115
+ "403": {
13116
+ "description": "ForbiddenError 403 response",
13117
+ "content": {
13118
+ "application/json": {
13119
+ "schema": {
13120
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
13121
+ }
13122
+ }
13123
+ }
13124
+ },
13125
+ "404": {
13126
+ "description": "NotFoundError 404 response",
13127
+ "content": {
13128
+ "application/json": {
13129
+ "schema": {
13130
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
13131
+ }
13132
+ }
13133
+ }
13134
+ },
13135
+ "429": {
13136
+ "description": "RateLimitError 429 response",
13137
+ "content": {
13138
+ "application/json": {
13139
+ "schema": {
13140
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
13141
+ }
13142
+ }
13143
+ }
13144
+ },
13145
+ "500": {
13146
+ "description": "InternalServerError 500 response",
13147
+ "content": {
13148
+ "application/json": {
13149
+ "schema": {
13150
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
13151
+ }
13152
+ }
13153
+ }
13154
+ }
13155
+ },
13156
+ "tags": [
13157
+ "Projects"
13158
+ ],
13159
+ "x-basecamp-idempotent": {
13160
+ "natural": true
13161
+ },
13162
+ "x-basecamp-retry": {
13163
+ "maxAttempts": 3,
13164
+ "baseDelayMs": 1000,
13165
+ "backoff": "exponential",
13166
+ "retryOn": [
13167
+ 429,
13168
+ 503
13169
+ ]
13170
+ }
13171
+ }
13172
+ },
13015
13173
  "/projects/{projectId}/status/active.json": {
13016
13174
  "put": {
13017
13175
  "description": "Restore a project to active status from trash as well as from the archive (returns 204 No Content).\nThis is the inverse of both ArchiveProject and TrashProject. Restoring counts against\nthe account's project limit, so it answers 507 when that limit is already reached.",
@@ -15321,20 +15479,10 @@
15321
15479
  }
15322
15480
  }
15323
15481
  },
15324
- "/recordings/{recordingId}/client_visibility.json": {
15325
- "put": {
15326
- "description": "Set client visibility for a recording",
15327
- "operationId": "SetClientVisibility",
15328
- "requestBody": {
15329
- "content": {
15330
- "application/json": {
15331
- "schema": {
15332
- "$ref": "#/components/schemas/SetClientVisibilityRequestContent"
15333
- }
15334
- }
15335
- },
15336
- "required": true
15337
- },
15482
+ "/recordings/{recordingId}/bubble_up.json": {
15483
+ "delete": {
15484
+ "description": "Remove the current user's bubble-up from a recording (returns 204 No Content).\nIdempotent: popping an absent bubble-up also returns 204.",
15485
+ "operationId": "DeleteBubbleUp",
15338
15486
  "parameters": [
15339
15487
  {
15340
15488
  "name": "recordingId",
@@ -15347,15 +15495,8 @@
15347
15495
  }
15348
15496
  ],
15349
15497
  "responses": {
15350
- "200": {
15351
- "description": "SetClientVisibility 200 response",
15352
- "content": {
15353
- "application/json": {
15354
- "schema": {
15355
- "$ref": "#/components/schemas/SetClientVisibilityResponseContent"
15356
- }
15357
- }
15358
- }
15498
+ "204": {
15499
+ "description": "DeleteBubbleUp 204 response"
15359
15500
  },
15360
15501
  "401": {
15361
15502
  "description": "UnauthorizedError 401 response",
@@ -15387,12 +15528,12 @@
15387
15528
  }
15388
15529
  }
15389
15530
  },
15390
- "422": {
15391
- "description": "ValidationError 422 response",
15531
+ "429": {
15532
+ "description": "RateLimitError 429 response",
15392
15533
  "content": {
15393
15534
  "application/json": {
15394
15535
  "schema": {
15395
- "$ref": "#/components/schemas/ValidationErrorResponseContent"
15536
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
15396
15537
  }
15397
15538
  }
15398
15539
  }
@@ -15409,7 +15550,7 @@
15409
15550
  }
15410
15551
  },
15411
15552
  "tags": [
15412
- "ClientFeatures"
15553
+ "BubbleUps"
15413
15554
  ],
15414
15555
  "x-basecamp-idempotent": {
15415
15556
  "natural": true
@@ -15423,12 +15564,19 @@
15423
15564
  503
15424
15565
  ]
15425
15566
  }
15426
- }
15427
- },
15428
- "/recordings/{recordingId}/comments.json": {
15429
- "get": {
15430
- "description": "List comments on a recording\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.",
15431
- "operationId": "ListComments",
15567
+ },
15568
+ "post": {
15569
+ "description": "Bubble up a recording for the current user, resurfacing it in the current\nuser's readings (the BC5 successor to \"save\"). Returns 204 No Content with\nno body.\n\nThe `at` field controls timing. Send `\"now\"` to bubble up immediately, or a\nscheduling keyword (`\"today\"`, `\"tomorrow\"`, `\"weekend\"`, `\"next_week\"`) or\nan ISO8601 date (e.g. `\"2026-09-10\"`) to schedule it to resurface later.\nNOTE: bc3 currently requires `at` — omitting it raises on the server\n(`Date.iso8601(nil)`), so send `\"now\"` for the immediate case. The field is\nmodeled optional (not `@required`) so a future bc3 default (`params[:at] ||=\n\"now\"`) makes omission mean \"now\" without an SDK change.\n\nIdempotent: bubbling up an already-bubbled recording is set-membership and\nstill returns 204.",
15570
+ "operationId": "CreateBubbleUp",
15571
+ "requestBody": {
15572
+ "content": {
15573
+ "application/json": {
15574
+ "schema": {
15575
+ "$ref": "#/components/schemas/CreateBubbleUpRequestContent"
15576
+ }
15577
+ }
15578
+ }
15579
+ },
15432
15580
  "parameters": [
15433
15581
  {
15434
15582
  "name": "recordingId",
@@ -15438,28 +15586,11 @@
15438
15586
  "format": "int64"
15439
15587
  },
15440
15588
  "required": true
15441
- },
15442
- {
15443
- "name": "page",
15444
- "in": "query",
15445
- "description": "Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.",
15446
- "schema": {
15447
- "type": "integer",
15448
- "description": "Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.",
15449
- "format": "int32"
15450
- }
15451
15589
  }
15452
15590
  ],
15453
15591
  "responses": {
15454
- "200": {
15455
- "description": "ListComments 200 response",
15456
- "content": {
15457
- "application/json": {
15458
- "schema": {
15459
- "$ref": "#/components/schemas/ListCommentsResponseContent"
15460
- }
15461
- }
15462
- }
15592
+ "204": {
15593
+ "description": "CreateBubbleUp 204 response"
15463
15594
  },
15464
15595
  "401": {
15465
15596
  "description": "UnauthorizedError 401 response",
@@ -15481,6 +15612,16 @@
15481
15612
  }
15482
15613
  }
15483
15614
  },
15615
+ "404": {
15616
+ "description": "NotFoundError 404 response",
15617
+ "content": {
15618
+ "application/json": {
15619
+ "schema": {
15620
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
15621
+ }
15622
+ }
15623
+ }
15624
+ },
15484
15625
  "429": {
15485
15626
  "description": "RateLimitError 429 response",
15486
15627
  "content": {
@@ -15503,12 +15644,10 @@
15503
15644
  }
15504
15645
  },
15505
15646
  "tags": [
15506
- "Messages"
15647
+ "BubbleUps"
15507
15648
  ],
15508
- "x-basecamp-pagination": {
15509
- "style": "link",
15510
- "totalCountHeader": "X-Total-Count",
15511
- "maxPageSize": 50
15649
+ "x-basecamp-idempotent": {
15650
+ "natural": true
15512
15651
  },
15513
15652
  "x-basecamp-retry": {
15514
15653
  "maxAttempts": 3,
@@ -15519,15 +15658,17 @@
15519
15658
  503
15520
15659
  ]
15521
15660
  }
15522
- },
15523
- "post": {
15524
- "description": "Create a new comment on a recording",
15525
- "operationId": "CreateComment",
15661
+ }
15662
+ },
15663
+ "/recordings/{recordingId}/client_visibility.json": {
15664
+ "put": {
15665
+ "description": "Set client visibility for a recording",
15666
+ "operationId": "SetClientVisibility",
15526
15667
  "requestBody": {
15527
15668
  "content": {
15528
15669
  "application/json": {
15529
15670
  "schema": {
15530
- "$ref": "#/components/schemas/CreateCommentRequestContent"
15671
+ "$ref": "#/components/schemas/SetClientVisibilityRequestContent"
15531
15672
  }
15532
15673
  }
15533
15674
  },
@@ -15545,12 +15686,12 @@
15545
15686
  }
15546
15687
  ],
15547
15688
  "responses": {
15548
- "201": {
15549
- "description": "CreateComment 201 response",
15689
+ "200": {
15690
+ "description": "SetClientVisibility 200 response",
15550
15691
  "content": {
15551
15692
  "application/json": {
15552
15693
  "schema": {
15553
- "$ref": "#/components/schemas/CreateCommentResponseContent"
15694
+ "$ref": "#/components/schemas/SetClientVisibilityResponseContent"
15554
15695
  }
15555
15696
  }
15556
15697
  }
@@ -15575,8 +15716,206 @@
15575
15716
  }
15576
15717
  }
15577
15718
  },
15578
- "422": {
15579
- "description": "ValidationError 422 response",
15719
+ "404": {
15720
+ "description": "NotFoundError 404 response",
15721
+ "content": {
15722
+ "application/json": {
15723
+ "schema": {
15724
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
15725
+ }
15726
+ }
15727
+ }
15728
+ },
15729
+ "422": {
15730
+ "description": "ValidationError 422 response",
15731
+ "content": {
15732
+ "application/json": {
15733
+ "schema": {
15734
+ "$ref": "#/components/schemas/ValidationErrorResponseContent"
15735
+ }
15736
+ }
15737
+ }
15738
+ },
15739
+ "500": {
15740
+ "description": "InternalServerError 500 response",
15741
+ "content": {
15742
+ "application/json": {
15743
+ "schema": {
15744
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
15745
+ }
15746
+ }
15747
+ }
15748
+ }
15749
+ },
15750
+ "tags": [
15751
+ "ClientFeatures"
15752
+ ],
15753
+ "x-basecamp-idempotent": {
15754
+ "natural": true
15755
+ },
15756
+ "x-basecamp-retry": {
15757
+ "maxAttempts": 3,
15758
+ "baseDelayMs": 1000,
15759
+ "backoff": "exponential",
15760
+ "retryOn": [
15761
+ 429,
15762
+ 503
15763
+ ]
15764
+ }
15765
+ }
15766
+ },
15767
+ "/recordings/{recordingId}/comments.json": {
15768
+ "get": {
15769
+ "description": "List comments on a recording\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.",
15770
+ "operationId": "ListComments",
15771
+ "parameters": [
15772
+ {
15773
+ "name": "recordingId",
15774
+ "in": "path",
15775
+ "schema": {
15776
+ "type": "integer",
15777
+ "format": "int64"
15778
+ },
15779
+ "required": true
15780
+ },
15781
+ {
15782
+ "name": "page",
15783
+ "in": "query",
15784
+ "description": "Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.",
15785
+ "schema": {
15786
+ "type": "integer",
15787
+ "description": "Page number for paginating through results. Defaults to 1. A positive value selects exactly that page, not a starting offset; see SPEC section 8.",
15788
+ "format": "int32"
15789
+ }
15790
+ }
15791
+ ],
15792
+ "responses": {
15793
+ "200": {
15794
+ "description": "ListComments 200 response",
15795
+ "content": {
15796
+ "application/json": {
15797
+ "schema": {
15798
+ "$ref": "#/components/schemas/ListCommentsResponseContent"
15799
+ }
15800
+ }
15801
+ }
15802
+ },
15803
+ "401": {
15804
+ "description": "UnauthorizedError 401 response",
15805
+ "content": {
15806
+ "application/json": {
15807
+ "schema": {
15808
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
15809
+ }
15810
+ }
15811
+ }
15812
+ },
15813
+ "403": {
15814
+ "description": "ForbiddenError 403 response",
15815
+ "content": {
15816
+ "application/json": {
15817
+ "schema": {
15818
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
15819
+ }
15820
+ }
15821
+ }
15822
+ },
15823
+ "429": {
15824
+ "description": "RateLimitError 429 response",
15825
+ "content": {
15826
+ "application/json": {
15827
+ "schema": {
15828
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
15829
+ }
15830
+ }
15831
+ }
15832
+ },
15833
+ "500": {
15834
+ "description": "InternalServerError 500 response",
15835
+ "content": {
15836
+ "application/json": {
15837
+ "schema": {
15838
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
15839
+ }
15840
+ }
15841
+ }
15842
+ }
15843
+ },
15844
+ "tags": [
15845
+ "Messages"
15846
+ ],
15847
+ "x-basecamp-pagination": {
15848
+ "style": "link",
15849
+ "totalCountHeader": "X-Total-Count",
15850
+ "maxPageSize": 50
15851
+ },
15852
+ "x-basecamp-retry": {
15853
+ "maxAttempts": 3,
15854
+ "baseDelayMs": 1000,
15855
+ "backoff": "exponential",
15856
+ "retryOn": [
15857
+ 429,
15858
+ 503
15859
+ ]
15860
+ }
15861
+ },
15862
+ "post": {
15863
+ "description": "Create a new comment on a recording",
15864
+ "operationId": "CreateComment",
15865
+ "requestBody": {
15866
+ "content": {
15867
+ "application/json": {
15868
+ "schema": {
15869
+ "$ref": "#/components/schemas/CreateCommentRequestContent"
15870
+ }
15871
+ }
15872
+ },
15873
+ "required": true
15874
+ },
15875
+ "parameters": [
15876
+ {
15877
+ "name": "recordingId",
15878
+ "in": "path",
15879
+ "schema": {
15880
+ "type": "integer",
15881
+ "format": "int64"
15882
+ },
15883
+ "required": true
15884
+ }
15885
+ ],
15886
+ "responses": {
15887
+ "201": {
15888
+ "description": "CreateComment 201 response",
15889
+ "content": {
15890
+ "application/json": {
15891
+ "schema": {
15892
+ "$ref": "#/components/schemas/CreateCommentResponseContent"
15893
+ }
15894
+ }
15895
+ }
15896
+ },
15897
+ "401": {
15898
+ "description": "UnauthorizedError 401 response",
15899
+ "content": {
15900
+ "application/json": {
15901
+ "schema": {
15902
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
15903
+ }
15904
+ }
15905
+ }
15906
+ },
15907
+ "403": {
15908
+ "description": "ForbiddenError 403 response",
15909
+ "content": {
15910
+ "application/json": {
15911
+ "schema": {
15912
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
15913
+ }
15914
+ }
15915
+ }
15916
+ },
15917
+ "422": {
15918
+ "description": "ValidationError 422 response",
15580
15919
  "content": {
15581
15920
  "application/json": {
15582
15921
  "schema": {
@@ -15929,10 +16268,10 @@
15929
16268
  }
15930
16269
  }
15931
16270
  },
15932
- "/recordings/{recordingId}/status/active.json": {
15933
- "put": {
15934
- "description": "Unarchive a recording (restore to active status)",
15935
- "operationId": "UnarchiveRecording",
16271
+ "/recordings/{recordingId}/spotlight.json": {
16272
+ "delete": {
16273
+ "description": "Remove a recording from the spotlight area.\nIdempotent: removing an absent spotlight also returns 204.",
16274
+ "operationId": "UnspotlightRecording",
15936
16275
  "parameters": [
15937
16276
  {
15938
16277
  "name": "recordingId",
@@ -15946,7 +16285,7 @@
15946
16285
  ],
15947
16286
  "responses": {
15948
16287
  "204": {
15949
- "description": "UnarchiveRecording 204 response"
16288
+ "description": "UnspotlightRecording 204 response"
15950
16289
  },
15951
16290
  "401": {
15952
16291
  "description": "UnauthorizedError 401 response",
@@ -15978,12 +16317,12 @@
15978
16317
  }
15979
16318
  }
15980
16319
  },
15981
- "422": {
15982
- "description": "ValidationError 422 response",
16320
+ "429": {
16321
+ "description": "RateLimitError 429 response",
15983
16322
  "content": {
15984
16323
  "application/json": {
15985
16324
  "schema": {
15986
- "$ref": "#/components/schemas/ValidationErrorResponseContent"
16325
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
15987
16326
  }
15988
16327
  }
15989
16328
  }
@@ -16014,12 +16353,10 @@
16014
16353
  503
16015
16354
  ]
16016
16355
  }
16017
- }
16018
- },
16019
- "/recordings/{recordingId}/status/archived.json": {
16020
- "put": {
16021
- "description": "Archive a recording",
16022
- "operationId": "ArchiveRecording",
16356
+ },
16357
+ "post": {
16358
+ "description": "Put a recording's card in the spotlight area on its project or template home page.\nIdempotent: spotlighting an already-spotlighted recording still returns 201.",
16359
+ "operationId": "SpotlightRecording",
16023
16360
  "parameters": [
16024
16361
  {
16025
16362
  "name": "recordingId",
@@ -16032,8 +16369,15 @@
16032
16369
  }
16033
16370
  ],
16034
16371
  "responses": {
16035
- "204": {
16036
- "description": "ArchiveRecording 204 response"
16372
+ "201": {
16373
+ "description": "SpotlightRecording 201 response",
16374
+ "content": {
16375
+ "application/json": {
16376
+ "schema": {
16377
+ "$ref": "#/components/schemas/SpotlightRecordingResponseContent"
16378
+ }
16379
+ }
16380
+ }
16037
16381
  },
16038
16382
  "401": {
16039
16383
  "description": "UnauthorizedError 401 response",
@@ -16075,6 +16419,16 @@
16075
16419
  }
16076
16420
  }
16077
16421
  },
16422
+ "429": {
16423
+ "description": "RateLimitError 429 response",
16424
+ "content": {
16425
+ "application/json": {
16426
+ "schema": {
16427
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
16428
+ }
16429
+ }
16430
+ }
16431
+ },
16078
16432
  "500": {
16079
16433
  "description": "InternalServerError 500 response",
16080
16434
  "content": {
@@ -16103,10 +16457,10 @@
16103
16457
  }
16104
16458
  }
16105
16459
  },
16106
- "/recordings/{recordingId}/status/trashed.json": {
16460
+ "/recordings/{recordingId}/status/active.json": {
16107
16461
  "put": {
16108
- "description": "Trash a recording",
16109
- "operationId": "TrashRecording",
16462
+ "description": "Unarchive a recording (restore to active status)",
16463
+ "operationId": "UnarchiveRecording",
16110
16464
  "parameters": [
16111
16465
  {
16112
16466
  "name": "recordingId",
@@ -16115,19 +16469,12 @@
16115
16469
  "type": "integer",
16116
16470
  "format": "int64"
16117
16471
  },
16118
- "required": true,
16119
- "examples": {
16120
- "TrashRecording_example1": {
16121
- "summary": "Trash any recording type",
16122
- "description": "Works on comments, messages, documents, cards - any recording",
16123
- "value": 555666
16124
- }
16125
- }
16472
+ "required": true
16126
16473
  }
16127
16474
  ],
16128
16475
  "responses": {
16129
16476
  "204": {
16130
- "description": "TrashRecording 204 response"
16477
+ "description": "UnarchiveRecording 204 response"
16131
16478
  },
16132
16479
  "401": {
16133
16480
  "description": "UnauthorizedError 401 response",
@@ -16197,8 +16544,189 @@
16197
16544
  }
16198
16545
  }
16199
16546
  },
16200
- "/recordings/{recordingId}/subscription.json": {
16201
- "delete": {
16547
+ "/recordings/{recordingId}/status/archived.json": {
16548
+ "put": {
16549
+ "description": "Archive a recording",
16550
+ "operationId": "ArchiveRecording",
16551
+ "parameters": [
16552
+ {
16553
+ "name": "recordingId",
16554
+ "in": "path",
16555
+ "schema": {
16556
+ "type": "integer",
16557
+ "format": "int64"
16558
+ },
16559
+ "required": true
16560
+ }
16561
+ ],
16562
+ "responses": {
16563
+ "204": {
16564
+ "description": "ArchiveRecording 204 response"
16565
+ },
16566
+ "401": {
16567
+ "description": "UnauthorizedError 401 response",
16568
+ "content": {
16569
+ "application/json": {
16570
+ "schema": {
16571
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
16572
+ }
16573
+ }
16574
+ }
16575
+ },
16576
+ "403": {
16577
+ "description": "ForbiddenError 403 response",
16578
+ "content": {
16579
+ "application/json": {
16580
+ "schema": {
16581
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
16582
+ }
16583
+ }
16584
+ }
16585
+ },
16586
+ "404": {
16587
+ "description": "NotFoundError 404 response",
16588
+ "content": {
16589
+ "application/json": {
16590
+ "schema": {
16591
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
16592
+ }
16593
+ }
16594
+ }
16595
+ },
16596
+ "422": {
16597
+ "description": "ValidationError 422 response",
16598
+ "content": {
16599
+ "application/json": {
16600
+ "schema": {
16601
+ "$ref": "#/components/schemas/ValidationErrorResponseContent"
16602
+ }
16603
+ }
16604
+ }
16605
+ },
16606
+ "500": {
16607
+ "description": "InternalServerError 500 response",
16608
+ "content": {
16609
+ "application/json": {
16610
+ "schema": {
16611
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
16612
+ }
16613
+ }
16614
+ }
16615
+ }
16616
+ },
16617
+ "tags": [
16618
+ "Automation"
16619
+ ],
16620
+ "x-basecamp-idempotent": {
16621
+ "natural": true
16622
+ },
16623
+ "x-basecamp-retry": {
16624
+ "maxAttempts": 3,
16625
+ "baseDelayMs": 1000,
16626
+ "backoff": "exponential",
16627
+ "retryOn": [
16628
+ 429,
16629
+ 503
16630
+ ]
16631
+ }
16632
+ }
16633
+ },
16634
+ "/recordings/{recordingId}/status/trashed.json": {
16635
+ "put": {
16636
+ "description": "Trash a recording",
16637
+ "operationId": "TrashRecording",
16638
+ "parameters": [
16639
+ {
16640
+ "name": "recordingId",
16641
+ "in": "path",
16642
+ "schema": {
16643
+ "type": "integer",
16644
+ "format": "int64"
16645
+ },
16646
+ "required": true,
16647
+ "examples": {
16648
+ "TrashRecording_example1": {
16649
+ "summary": "Trash any recording type",
16650
+ "description": "Works on comments, messages, documents, cards - any recording",
16651
+ "value": 555666
16652
+ }
16653
+ }
16654
+ }
16655
+ ],
16656
+ "responses": {
16657
+ "204": {
16658
+ "description": "TrashRecording 204 response"
16659
+ },
16660
+ "401": {
16661
+ "description": "UnauthorizedError 401 response",
16662
+ "content": {
16663
+ "application/json": {
16664
+ "schema": {
16665
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
16666
+ }
16667
+ }
16668
+ }
16669
+ },
16670
+ "403": {
16671
+ "description": "ForbiddenError 403 response",
16672
+ "content": {
16673
+ "application/json": {
16674
+ "schema": {
16675
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
16676
+ }
16677
+ }
16678
+ }
16679
+ },
16680
+ "404": {
16681
+ "description": "NotFoundError 404 response",
16682
+ "content": {
16683
+ "application/json": {
16684
+ "schema": {
16685
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
16686
+ }
16687
+ }
16688
+ }
16689
+ },
16690
+ "422": {
16691
+ "description": "ValidationError 422 response",
16692
+ "content": {
16693
+ "application/json": {
16694
+ "schema": {
16695
+ "$ref": "#/components/schemas/ValidationErrorResponseContent"
16696
+ }
16697
+ }
16698
+ }
16699
+ },
16700
+ "500": {
16701
+ "description": "InternalServerError 500 response",
16702
+ "content": {
16703
+ "application/json": {
16704
+ "schema": {
16705
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
16706
+ }
16707
+ }
16708
+ }
16709
+ }
16710
+ },
16711
+ "tags": [
16712
+ "Automation"
16713
+ ],
16714
+ "x-basecamp-idempotent": {
16715
+ "natural": true
16716
+ },
16717
+ "x-basecamp-retry": {
16718
+ "maxAttempts": 3,
16719
+ "baseDelayMs": 1000,
16720
+ "backoff": "exponential",
16721
+ "retryOn": [
16722
+ 429,
16723
+ 503
16724
+ ]
16725
+ }
16726
+ }
16727
+ },
16728
+ "/recordings/{recordingId}/subscription.json": {
16729
+ "delete": {
16202
16730
  "description": "Unsubscribe the current user from a recording",
16203
16731
  "operationId": "Unsubscribe",
16204
16732
  "parameters": [
@@ -19070,7 +19598,281 @@
19070
19598
  "Folders"
19071
19599
  ],
19072
19600
  "x-basecamp-retry": {
19073
- "maxAttempts": 3,
19601
+ "maxAttempts": 3,
19602
+ "baseDelayMs": 1000,
19603
+ "backoff": "exponential",
19604
+ "retryOn": [
19605
+ 429,
19606
+ 503
19607
+ ]
19608
+ }
19609
+ },
19610
+ "put": {
19611
+ "description": "Rename a folder.\n\n`name` is the only writable attribute; a folder's projects, ordering, and\nimage are managed elsewhere and an image parameter sent here is ignored.",
19612
+ "operationId": "UpdateFolder",
19613
+ "requestBody": {
19614
+ "content": {
19615
+ "application/json": {
19616
+ "schema": {
19617
+ "$ref": "#/components/schemas/UpdateFolderRequestContent"
19618
+ }
19619
+ }
19620
+ },
19621
+ "required": true
19622
+ },
19623
+ "parameters": [
19624
+ {
19625
+ "name": "folderId",
19626
+ "in": "path",
19627
+ "schema": {
19628
+ "type": "integer",
19629
+ "format": "int64"
19630
+ },
19631
+ "required": true
19632
+ }
19633
+ ],
19634
+ "responses": {
19635
+ "200": {
19636
+ "description": "UpdateFolder 200 response",
19637
+ "content": {
19638
+ "application/json": {
19639
+ "schema": {
19640
+ "$ref": "#/components/schemas/UpdateFolderResponseContent"
19641
+ }
19642
+ }
19643
+ }
19644
+ },
19645
+ "401": {
19646
+ "description": "UnauthorizedError 401 response",
19647
+ "content": {
19648
+ "application/json": {
19649
+ "schema": {
19650
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
19651
+ }
19652
+ }
19653
+ }
19654
+ },
19655
+ "403": {
19656
+ "description": "ForbiddenError 403 response",
19657
+ "content": {
19658
+ "application/json": {
19659
+ "schema": {
19660
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
19661
+ }
19662
+ }
19663
+ }
19664
+ },
19665
+ "404": {
19666
+ "description": "NotFoundError 404 response",
19667
+ "content": {
19668
+ "application/json": {
19669
+ "schema": {
19670
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
19671
+ }
19672
+ }
19673
+ }
19674
+ },
19675
+ "422": {
19676
+ "description": "FieldValidationError 422 response",
19677
+ "content": {
19678
+ "application/json": {
19679
+ "schema": {
19680
+ "$ref": "#/components/schemas/FieldValidationErrorResponseContent"
19681
+ }
19682
+ }
19683
+ }
19684
+ },
19685
+ "500": {
19686
+ "description": "InternalServerError 500 response",
19687
+ "content": {
19688
+ "application/json": {
19689
+ "schema": {
19690
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
19691
+ }
19692
+ }
19693
+ }
19694
+ }
19695
+ },
19696
+ "tags": [
19697
+ "Folders"
19698
+ ],
19699
+ "x-basecamp-idempotent": {
19700
+ "natural": true
19701
+ },
19702
+ "x-basecamp-retry": {
19703
+ "maxAttempts": 3,
19704
+ "baseDelayMs": 1000,
19705
+ "backoff": "exponential",
19706
+ "retryOn": [
19707
+ 429,
19708
+ 503
19709
+ ]
19710
+ }
19711
+ }
19712
+ },
19713
+ "/template_library.json": {
19714
+ "get": {
19715
+ "description": "Get the account's to-do list template library",
19716
+ "operationId": "GetTemplateLibrary",
19717
+ "parameters": [],
19718
+ "responses": {
19719
+ "200": {
19720
+ "description": "GetTemplateLibrary 200 response",
19721
+ "content": {
19722
+ "application/json": {
19723
+ "schema": {
19724
+ "$ref": "#/components/schemas/GetTemplateLibraryResponseContent"
19725
+ }
19726
+ }
19727
+ }
19728
+ },
19729
+ "401": {
19730
+ "description": "UnauthorizedError 401 response",
19731
+ "content": {
19732
+ "application/json": {
19733
+ "schema": {
19734
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
19735
+ }
19736
+ }
19737
+ }
19738
+ },
19739
+ "403": {
19740
+ "description": "ForbiddenError 403 response",
19741
+ "content": {
19742
+ "application/json": {
19743
+ "schema": {
19744
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
19745
+ }
19746
+ }
19747
+ }
19748
+ },
19749
+ "429": {
19750
+ "description": "RateLimitError 429 response",
19751
+ "content": {
19752
+ "application/json": {
19753
+ "schema": {
19754
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
19755
+ }
19756
+ }
19757
+ }
19758
+ },
19759
+ "500": {
19760
+ "description": "InternalServerError 500 response",
19761
+ "content": {
19762
+ "application/json": {
19763
+ "schema": {
19764
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
19765
+ }
19766
+ }
19767
+ }
19768
+ }
19769
+ },
19770
+ "tags": [
19771
+ "Automation"
19772
+ ],
19773
+ "x-basecamp-retry": {
19774
+ "maxAttempts": 3,
19775
+ "baseDelayMs": 1000,
19776
+ "backoff": "exponential",
19777
+ "retryOn": [
19778
+ 429,
19779
+ 503
19780
+ ]
19781
+ }
19782
+ }
19783
+ },
19784
+ "/template_library/copies.json": {
19785
+ "post": {
19786
+ "description": "Start copying a to-do list template into a project",
19787
+ "operationId": "CreateTemplateLibraryCopy",
19788
+ "requestBody": {
19789
+ "content": {
19790
+ "application/json": {
19791
+ "schema": {
19792
+ "$ref": "#/components/schemas/CreateTemplateLibraryCopyRequestContent"
19793
+ }
19794
+ }
19795
+ },
19796
+ "required": true
19797
+ },
19798
+ "parameters": [],
19799
+ "responses": {
19800
+ "201": {
19801
+ "description": "CreateTemplateLibraryCopy 201 response",
19802
+ "content": {
19803
+ "application/json": {
19804
+ "schema": {
19805
+ "$ref": "#/components/schemas/CreateTemplateLibraryCopyResponseContent"
19806
+ }
19807
+ }
19808
+ }
19809
+ },
19810
+ "401": {
19811
+ "description": "UnauthorizedError 401 response",
19812
+ "content": {
19813
+ "application/json": {
19814
+ "schema": {
19815
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
19816
+ }
19817
+ }
19818
+ }
19819
+ },
19820
+ "403": {
19821
+ "description": "ForbiddenError 403 response",
19822
+ "content": {
19823
+ "application/json": {
19824
+ "schema": {
19825
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
19826
+ }
19827
+ }
19828
+ }
19829
+ },
19830
+ "404": {
19831
+ "description": "NotFoundError 404 response",
19832
+ "content": {
19833
+ "application/json": {
19834
+ "schema": {
19835
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
19836
+ }
19837
+ }
19838
+ }
19839
+ },
19840
+ "422": {
19841
+ "description": "PeopleConfirmationRequiredError 422 response",
19842
+ "content": {
19843
+ "application/json": {
19844
+ "schema": {
19845
+ "$ref": "#/components/schemas/PeopleConfirmationRequiredErrorResponseContent"
19846
+ }
19847
+ }
19848
+ }
19849
+ },
19850
+ "429": {
19851
+ "description": "RateLimitError 429 response",
19852
+ "content": {
19853
+ "application/json": {
19854
+ "schema": {
19855
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
19856
+ }
19857
+ }
19858
+ }
19859
+ },
19860
+ "500": {
19861
+ "description": "InternalServerError 500 response",
19862
+ "content": {
19863
+ "application/json": {
19864
+ "schema": {
19865
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
19866
+ }
19867
+ }
19868
+ }
19869
+ }
19870
+ },
19871
+ "tags": [
19872
+ "Automation"
19873
+ ],
19874
+ "x-basecamp-retry": {
19875
+ "maxAttempts": 2,
19074
19876
  "baseDelayMs": 1000,
19075
19877
  "backoff": "exponential",
19076
19878
  "retryOn": [
@@ -19078,23 +19880,15 @@
19078
19880
  503
19079
19881
  ]
19080
19882
  }
19081
- },
19082
- "put": {
19083
- "description": "Rename a folder.\n\n`name` is the only writable attribute; a folder's projects, ordering, and\nimage are managed elsewhere and an image parameter sent here is ignored.",
19084
- "operationId": "UpdateFolder",
19085
- "requestBody": {
19086
- "content": {
19087
- "application/json": {
19088
- "schema": {
19089
- "$ref": "#/components/schemas/UpdateFolderRequestContent"
19090
- }
19091
- }
19092
- },
19093
- "required": true
19094
- },
19883
+ }
19884
+ },
19885
+ "/template_library/copies/{copyId}": {
19886
+ "get": {
19887
+ "description": "Get the current status of a to-do list template copy",
19888
+ "operationId": "GetTemplateLibraryCopy",
19095
19889
  "parameters": [
19096
19890
  {
19097
- "name": "folderId",
19891
+ "name": "copyId",
19098
19892
  "in": "path",
19099
19893
  "schema": {
19100
19894
  "type": "integer",
@@ -19105,11 +19899,11 @@
19105
19899
  ],
19106
19900
  "responses": {
19107
19901
  "200": {
19108
- "description": "UpdateFolder 200 response",
19902
+ "description": "GetTemplateLibraryCopy 200 response",
19109
19903
  "content": {
19110
19904
  "application/json": {
19111
19905
  "schema": {
19112
- "$ref": "#/components/schemas/UpdateFolderResponseContent"
19906
+ "$ref": "#/components/schemas/GetTemplateLibraryCopyResponseContent"
19113
19907
  }
19114
19908
  }
19115
19909
  }
@@ -19144,12 +19938,12 @@
19144
19938
  }
19145
19939
  }
19146
19940
  },
19147
- "422": {
19148
- "description": "FieldValidationError 422 response",
19941
+ "429": {
19942
+ "description": "RateLimitError 429 response",
19149
19943
  "content": {
19150
19944
  "application/json": {
19151
19945
  "schema": {
19152
- "$ref": "#/components/schemas/FieldValidationErrorResponseContent"
19946
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
19153
19947
  }
19154
19948
  }
19155
19949
  }
@@ -19166,11 +19960,8 @@
19166
19960
  }
19167
19961
  },
19168
19962
  "tags": [
19169
- "Folders"
19963
+ "Automation"
19170
19964
  ],
19171
- "x-basecamp-idempotent": {
19172
- "natural": true
19173
- },
19174
19965
  "x-basecamp-retry": {
19175
19966
  "maxAttempts": 3,
19176
19967
  "baseDelayMs": 1000,
@@ -24904,7 +25695,8 @@
24904
25695
  "type": "string"
24905
25696
  },
24906
25697
  "command_url": {
24907
- "type": "string"
25698
+ "type": "string",
25699
+ "description": "Only present when the requester is an account administrator; possession of this URL is enough to command the bot."
24908
25700
  },
24909
25701
  "url": {
24910
25702
  "type": "string"
@@ -24913,7 +25705,8 @@
24913
25705
  "type": "string"
24914
25706
  },
24915
25707
  "lines_url": {
24916
- "type": "string"
25708
+ "type": "string",
25709
+ "description": "Only present when the requester is an account administrator; possession of this URL is enough to post lines as the bot."
24917
25710
  }
24918
25711
  },
24919
25712
  "required": [
@@ -25551,6 +26344,15 @@
25551
26344
  "CreateBookmarkResponseContent": {
25552
26345
  "$ref": "#/components/schemas/Bookmark"
25553
26346
  },
26347
+ "CreateBubbleUpRequestContent": {
26348
+ "type": "object",
26349
+ "properties": {
26350
+ "at": {
26351
+ "type": "string",
26352
+ "description": "Timing for the bubble-up. `\"now\"` bubbles up immediately; a scheduling\nkeyword (`\"today\"`, `\"tomorrow\"`, `\"weekend\"`, `\"next_week\"`) or an ISO8601\ndate (e.g. `\"2026-09-10\"`) schedules it to resurface later. bc3 requires a\nvalue — omitting `at` errors server-side (`Date.iso8601(nil)`) — so send\n`\"now\"` for the immediate case."
26353
+ }
26354
+ }
26355
+ },
25554
26356
  "CreateCampfireLineRequestContent": {
25555
26357
  "type": "object",
25556
26358
  "properties": {
@@ -26069,6 +26871,30 @@
26069
26871
  "CreateScheduleEntryResponseContent": {
26070
26872
  "$ref": "#/components/schemas/ScheduleEntry"
26071
26873
  },
26874
+ "CreateTemplateLibraryCopyRequestContent": {
26875
+ "type": "object",
26876
+ "properties": {
26877
+ "template_recording_id": {
26878
+ "type": "integer",
26879
+ "format": "int64"
26880
+ },
26881
+ "destination_parent_id": {
26882
+ "type": "integer",
26883
+ "format": "int64"
26884
+ },
26885
+ "adding_people_confirmed": {
26886
+ "type": "boolean",
26887
+ "description": "Confirm granting destination-project access to people referenced by the template."
26888
+ }
26889
+ },
26890
+ "required": [
26891
+ "destination_parent_id",
26892
+ "template_recording_id"
26893
+ ]
26894
+ },
26895
+ "CreateTemplateLibraryCopyResponseContent": {
26896
+ "$ref": "#/components/schemas/TemplateLibraryCopy"
26897
+ },
26072
26898
  "CreateTemplateRequestContent": {
26073
26899
  "type": "object",
26074
26900
  "properties": {
@@ -27886,10 +28712,13 @@
27886
28712
  "type": "array",
27887
28713
  "items": {
27888
28714
  "$ref": "#/components/schemas/TimelineEvent"
27889
- },
27890
- "x-go-type-skip-optional-pointer": true
28715
+ }
27891
28716
  }
27892
- }
28717
+ },
28718
+ "required": [
28719
+ "events",
28720
+ "person"
28721
+ ]
27893
28722
  },
27894
28723
  "GetPersonResponseContent": {
27895
28724
  "$ref": "#/components/schemas/Person"
@@ -27951,6 +28780,12 @@
27951
28780
  "GetSubscriptionResponseContent": {
27952
28781
  "$ref": "#/components/schemas/Subscription"
27953
28782
  },
28783
+ "GetTemplateLibraryCopyResponseContent": {
28784
+ "$ref": "#/components/schemas/TemplateLibraryCopy"
28785
+ },
28786
+ "GetTemplateLibraryResponseContent": {
28787
+ "$ref": "#/components/schemas/TemplateLibrary"
28788
+ },
27954
28789
  "GetTemplateResponseContent": {
27955
28790
  "$ref": "#/components/schemas/Template"
27956
28791
  },
@@ -28497,6 +29332,12 @@
28497
29332
  "$ref": "#/components/schemas/Question"
28498
29333
  }
28499
29334
  },
29335
+ "ListRecentProjectsResponseContent": {
29336
+ "type": "array",
29337
+ "items": {
29338
+ "$ref": "#/components/schemas/Project"
29339
+ }
29340
+ },
28500
29341
  "ListRecordingBoostsResponseContent": {
28501
29342
  "type": "array",
28502
29343
  "items": {
@@ -28911,6 +29752,7 @@
28911
29752
  },
28912
29753
  "MyAssignmentAssignee": {
28913
29754
  "type": "object",
29755
+ "description": "A person as bc3's `people/_person_minimal.json.jbuilder` renders them —\nthe same three-key partial behind UpcomingSchedulePerson and\nOutOfOfficePerson. All three keys are emitted unconditionally, so all\nthree are required.",
28914
29756
  "properties": {
28915
29757
  "id": {
28916
29758
  "type": "integer",
@@ -28926,7 +29768,9 @@
28926
29768
  }
28927
29769
  },
28928
29770
  "required": [
28929
- "id"
29771
+ "avatar_url",
29772
+ "id",
29773
+ "name"
28930
29774
  ]
28931
29775
  },
28932
29776
  "MyAssignmentBucket": {
@@ -29219,6 +30063,7 @@
29219
30063
  },
29220
30064
  "OutOfOfficePerson": {
29221
30065
  "type": "object",
30066
+ "description": "A person as bc3's `people/_person_minimal.json.jbuilder` renders them —\nthe same three-key partial behind UpcomingSchedulePerson and\nMyAssignmentAssignee. All three keys are emitted unconditionally, so all\nthree are required.",
29222
30067
  "properties": {
29223
30068
  "id": {
29224
30069
  "type": "integer",
@@ -29234,7 +30079,9 @@
29234
30079
  }
29235
30080
  },
29236
30081
  "required": [
29237
- "id"
30082
+ "avatar_url",
30083
+ "id",
30084
+ "name"
29238
30085
  ]
29239
30086
  },
29240
30087
  "PauseQuestionResponseContent": {
@@ -29245,6 +30092,25 @@
29245
30092
  }
29246
30093
  }
29247
30094
  },
30095
+ "PeopleConfirmationRequiredErrorResponseContent": {
30096
+ "type": "object",
30097
+ "description": "The copy requires confirmation before granting destination-project access\nto people referenced by the template.",
30098
+ "properties": {
30099
+ "error": {
30100
+ "type": "string"
30101
+ },
30102
+ "people": {
30103
+ "type": "array",
30104
+ "items": {
30105
+ "$ref": "#/components/schemas/TemplateLibraryConfirmationPerson"
30106
+ }
30107
+ }
30108
+ },
30109
+ "required": [
30110
+ "error",
30111
+ "people"
30112
+ ]
30113
+ },
29248
30114
  "Person": {
29249
30115
  "type": "object",
29250
30116
  "properties": {
@@ -29519,6 +30385,10 @@
29519
30385
  "bookmark_url": {
29520
30386
  "type": "string"
29521
30387
  },
30388
+ "star_url": {
30389
+ "type": "string",
30390
+ "description": "Bucket stars collection for this project (`/buckets/{id}/stars.json`)."
30391
+ },
29522
30392
  "url": {
29523
30393
  "type": "string"
29524
30394
  },
@@ -29533,7 +30403,12 @@
29533
30403
  "x-go-type-skip-optional-pointer": true
29534
30404
  },
29535
30405
  "bookmarked": {
29536
- "type": "boolean"
30406
+ "type": "boolean",
30407
+ "description": "True when the project is pinned on the current user's home page at all,\nwhether starred or filed into a stack."
30408
+ },
30409
+ "starred": {
30410
+ "type": "boolean",
30411
+ "description": "True only when the project carries a star on the current user's home page."
29537
30412
  },
29538
30413
  "client_company": {
29539
30414
  "$ref": "#/components/schemas/ClientCompany"
@@ -30795,9 +31670,11 @@
30795
31670
  },
30796
31671
  "SearchResult": {
30797
31672
  "type": "object",
31673
+ "description": "One hit from account-wide search — a polymorphic projection over every\nsearchable recording type.\n\nMost result types render the common recording envelope\n(`recordings/_recording.json.jbuilder`) plus their own recordable partial,\nbut `api_search_result_template_path` special-cases four branches — chat\nlines, kanban (card table) lists, file attachments and gauge needles — and\nthe file-attachment branch writes its own projection from scratch instead\nof the envelope. Members are therefore optional unless every branch emits\nthem; the doc comments name the branches that carry each optional member.",
30798
31674
  "properties": {
30799
31675
  "id": {
30800
31676
  "type": "integer",
31677
+ "description": "The recording id. Optional only because the file-attachment branch\n(`searches/_attachment.json.jbuilder`) skips the recording envelope and\nemits none of the top-level id/title/type/url/app_url keys; every other\nbranch emits all five.",
30801
31678
  "format": "int64"
30802
31679
  },
30803
31680
  "status": {
@@ -30810,99 +31687,308 @@
30810
31687
  "type": "string",
30811
31688
  "x-go-type": "time.Time",
30812
31689
  "x-go-type-import": {
30813
- "path": "time"
31690
+ "path": "time"
31691
+ }
31692
+ },
31693
+ "updated_at": {
31694
+ "type": "string",
31695
+ "x-go-type": "time.Time",
31696
+ "x-go-type-import": {
31697
+ "path": "time"
31698
+ }
31699
+ },
31700
+ "title": {
31701
+ "type": "string",
31702
+ "description": "See `id` — omitted by the file-attachment branch, emitted by every other\nbranch."
31703
+ },
31704
+ "inherits_status": {
31705
+ "type": "boolean"
31706
+ },
31707
+ "type": {
31708
+ "type": "string",
31709
+ "description": "See `id` — omitted by the file-attachment branch, emitted by every other\nbranch. A file-attachment hit is therefore recognizable by the absence\nof this key (its file keys, `filename` through `app_download_url`, are\nthe positive signal)."
31710
+ },
31711
+ "url": {
31712
+ "type": "string",
31713
+ "description": "See `id` — omitted by the file-attachment branch, emitted by every other\nbranch."
31714
+ },
31715
+ "app_url": {
31716
+ "type": "string",
31717
+ "description": "See `id` — omitted by the file-attachment branch, emitted by every other\nbranch."
31718
+ },
31719
+ "bookmark_url": {
31720
+ "type": "string"
31721
+ },
31722
+ "subscription_url": {
31723
+ "type": "string",
31724
+ "description": "Subscription URL, emitted by the common recording envelope for any\nsubscribable result — kanban lists and gauge needles among the special\nbranches, plus subscribable generic-branch types (messages, todos, …)."
31725
+ },
31726
+ "bubble_up_url": {
31727
+ "type": "string",
31728
+ "description": "URL of the Bubble Up record for this recording (BC5 addition). Optional\nhere because this is a polymorphic projection:\n`recordings/_recording.json.jbuilder` emits the key only when the caller\npasses `local_assigns[:bubbleupable]`, and `todolists/_todolist` is the\nonly partial that does. So a Todolist-shaped instance carries it and the\nother recording types do not."
31729
+ },
31730
+ "parent": {
31731
+ "$ref": "#/components/schemas/RecordingParent"
31732
+ },
31733
+ "bucket": {
31734
+ "$ref": "#/components/schemas/RecordingBucket"
31735
+ },
31736
+ "creator": {
31737
+ "$ref": "#/components/schemas/Person"
31738
+ },
31739
+ "content": {
31740
+ "type": [
31741
+ "string",
31742
+ "null"
31743
+ ],
31744
+ "description": "Always present, always null. `api/searches/show.json.jbuilder` renders the\nrecording's own partial and then unconditionally overwrites `content` with\n`nil` to strip the large HTML body out of the search payload. The key is\ntherefore guaranteed present on every result — required — and its value is\nguaranteed null. Read `plain_text_content` instead.",
31745
+ "x-go-type": "string"
31746
+ },
31747
+ "description": {
31748
+ "type": [
31749
+ "string",
31750
+ "null"
31751
+ ],
31752
+ "description": "Always present, always null — the description-attribute counterpart to\n`content`. Read `plain_text_description` instead.",
31753
+ "x-go-type": "string"
31754
+ },
31755
+ "plain_text_content": {
31756
+ "type": "string",
31757
+ "description": "A highlighted, truncated excerpt of the recording's content — **not** plain\ntext despite the name. `excerpt_and_highlight_matches` converts the rich\ntext with `to_plain_text`, escapes it with `html_escape_once`, then wraps\neach query match in `<mark class=\"circled-text\"><span></span>…</mark>` and\ntruncates the result to 300 characters. Treat it as an HTML fragment.\n\nOptional and non-nullable: emitted only when the underlying recordable\nresponds to `content`, so a result whose type has no content attribute\nomits the key entirely rather than sending null."
31758
+ },
31759
+ "plain_text_description": {
31760
+ "type": "string",
31761
+ "description": "The description-attribute counterpart to `plain_text_content`, with the\nsame highlighting, escaping, and 300-character truncation. Optional and\nnon-nullable — omitted when the recordable has no description attribute."
31762
+ },
31763
+ "content_attachments": {
31764
+ "type": "array",
31765
+ "items": {
31766
+ "$ref": "#/components/schemas/RichTextAttachment"
31767
+ },
31768
+ "description": "Rich-text companion arrays carried through the polymorphic search\nprojection. A given result is one recording type, so it carries only\nthe array matching its rich-text attribute (`content_attachments` for a\nComment/Message, `description_attachments` for a Todo); a webhook-sourced\nresult carries neither. Optional (no `@required`), non-nullable.",
31769
+ "x-go-type-skip-optional-pointer": true
31770
+ },
31771
+ "description_attachments": {
31772
+ "type": "array",
31773
+ "items": {
31774
+ "$ref": "#/components/schemas/RichTextAttachment"
31775
+ },
31776
+ "description": "See `content_attachments` — the description-attribute companion array.",
31777
+ "x-go-type-skip-optional-pointer": true
31778
+ },
31779
+ "attachments": {
31780
+ "type": "array",
31781
+ "items": {
31782
+ "$ref": "#/components/schemas/SearchResultAttachment"
31783
+ },
31784
+ "description": "File attachments on the result, in either of two wire shapes — see\n`SearchResultAttachment`.\n\nFor a result whose recordable carries downloadable rich-text\nattachments, `searches/show.json.jbuilder` emits this key through the\nsame `attachments/_attachment` partial that builds the rich-text\ncompanion array above, so it repeats that array's elements. For a chat\n*upload* line, `chats/lines/_upload.json.jbuilder` instead builds a\nbespoke six-key aggregate inline — and because upload lines have no\nrich-text attribute, the show template never overwrites it, so that\ndistinct shape survives to the wire.",
31785
+ "x-go-type-skip-optional-pointer": true
31786
+ },
31787
+ "subject": {
31788
+ "type": "string"
31789
+ },
31790
+ "boosts_count": {
31791
+ "type": "integer",
31792
+ "description": "Boost count, emitted by the recording envelope when the branch passes\n`boostable` — chat lines and gauge needles.",
31793
+ "format": "int32"
31794
+ },
31795
+ "boosts_url": {
31796
+ "type": "string",
31797
+ "description": "See `boosts_count` — the companion URL."
31798
+ },
31799
+ "language": {
31800
+ "type": "string",
31801
+ "description": "Language of a code chat line (`chats/lines/_code.json.jbuilder`);\nvalidated present on the model, so never null when the key is emitted."
31802
+ },
31803
+ "image_url": {
31804
+ "type": "string",
31805
+ "description": "Image URL of a soundtracked (play-kind) chat line whose sound carries an\nimage; such a line emits `image_url` in place of `content`."
31806
+ },
31807
+ "sound_url": {
31808
+ "type": "string",
31809
+ "description": "Sound URL of a play-kind chat line; always emitted for that kind."
31810
+ },
31811
+ "subscribers": {
31812
+ "type": "array",
31813
+ "items": {
31814
+ "$ref": "#/components/schemas/Person"
31815
+ },
31816
+ "description": "Everyone subscribed to the kanban list, as full Person projections.",
31817
+ "x-go-type-skip-optional-pointer": true
31818
+ },
31819
+ "color": {
31820
+ "type": "string",
31821
+ "description": "Color of a kanban list or gauge needle. Emitted unconditionally by both\nbranches with a null value when unset, so it is nullable (the enhance\npass layers `nullable: true` onto the OpenAPI).",
31822
+ "nullable": true
31823
+ },
31824
+ "cards_count": {
31825
+ "type": "integer",
31826
+ "description": "Number of cards in the kanban list.",
31827
+ "format": "int32"
31828
+ },
31829
+ "comment_count": {
31830
+ "type": "integer",
31831
+ "description": "Comment count of a kanban list or gauge needle (branch-partial key,\nsingular `comment_count` — distinct from the envelope's\n`comments_count`, which a needle also carries).",
31832
+ "format": "int32"
31833
+ },
31834
+ "cards_url": {
31835
+ "type": "string",
31836
+ "description": "API URL of the kanban list's cards."
31837
+ },
31838
+ "on_hold": {
31839
+ "$ref": "#/components/schemas/CardColumnOnHold"
31840
+ },
31841
+ "comments_count": {
31842
+ "type": "integer",
31843
+ "description": "Comment count, emitted by the recording envelope when the branch passes\n`commentable` — gauge needles among the special branches, plus\ncommentable generic-branch types.",
31844
+ "format": "int32"
31845
+ },
31846
+ "comments_url": {
31847
+ "type": "string",
31848
+ "description": "See `comments_count` — the companion URL."
31849
+ },
31850
+ "position": {
31851
+ "type": "integer",
31852
+ "description": "Position of the result. Two emitters share the key: the recording\nenvelope emits list position for positioned recordings (kanban lists\namong the special branches), and the gauge-needle branch overwrites it\nwith the needle's own 0–100 gauge position.",
31853
+ "format": "int32"
31854
+ },
31855
+ "filename": {
31856
+ "type": "string",
31857
+ "description": "Filename of a file-attachment hit. This and the following file keys are\nemitted only by the file-attachment branch — the one branch that omits\nthe id/title/type/url/app_url envelope keys."
31858
+ },
31859
+ "content_type": {
31860
+ "type": "string",
31861
+ "description": "MIME type of a file-attachment hit."
31862
+ },
31863
+ "byte_size": {
31864
+ "type": "integer",
31865
+ "description": "Size in bytes of a file-attachment hit.",
31866
+ "format": "int64"
31867
+ },
31868
+ "previewable": {
31869
+ "type": "boolean",
31870
+ "description": "Whether the file can be previewed."
31871
+ },
31872
+ "width": {
31873
+ "type": "integer",
31874
+ "description": "Pixel width, emitted only when the file is previewable. May be\nfloat-spelled (`1024.0`) and nullable, like every other blob dimension —\nsee `RichTextAttachment.width` for the cross-SDK typing note.",
31875
+ "format": "int32",
31876
+ "nullable": true,
31877
+ "x-go-type": "types.FlexInt",
31878
+ "x-go-type-import": {
31879
+ "path": "github.com/basecamp/basecamp-sdk/go/pkg/types"
30814
31880
  }
30815
31881
  },
30816
- "updated_at": {
30817
- "type": "string",
30818
- "x-go-type": "time.Time",
31882
+ "height": {
31883
+ "type": "integer",
31884
+ "description": "See `width` — same conditional emission and nullable/float-spelled\nbehavior.",
31885
+ "format": "int32",
31886
+ "nullable": true,
31887
+ "x-go-type": "types.FlexInt",
30819
31888
  "x-go-type-import": {
30820
- "path": "time"
31889
+ "path": "github.com/basecamp/basecamp-sdk/go/pkg/types"
30821
31890
  }
30822
31891
  },
30823
- "title": {
30824
- "type": "string"
30825
- },
30826
- "inherits_status": {
30827
- "type": "boolean"
30828
- },
30829
- "type": {
30830
- "type": "string"
31892
+ "preview_url": {
31893
+ "type": "string",
31894
+ "description": "Full-size preview URL of a file-attachment hit."
30831
31895
  },
30832
- "url": {
30833
- "type": "string"
31896
+ "thumbnail_url": {
31897
+ "type": "string",
31898
+ "description": "Thumbnail URL of a file-attachment hit."
30834
31899
  },
30835
- "app_url": {
30836
- "type": "string"
31900
+ "download_url": {
31901
+ "type": "string",
31902
+ "description": "Authenticated download URL of a file-attachment hit.",
31903
+ "x-basecamp-auth-routable-url": {}
30837
31904
  },
30838
- "bookmark_url": {
30839
- "type": "string"
31905
+ "app_download_url": {
31906
+ "type": "string",
31907
+ "description": "Web (app-host) download URL of a file-attachment hit."
31908
+ }
31909
+ },
31910
+ "required": [
31911
+ "content",
31912
+ "description"
31913
+ ]
31914
+ },
31915
+ "SearchResultAttachment": {
31916
+ "type": "object",
31917
+ "description": "A file attached to a search result, in either of two wire shapes.\n\nThis is an optional-field superset over the two variants the search\nprojection emits (the TimelineAttachment approach): the rich-text\nattachment/blob shape rendered through `attachments/_attachment` +\n`blobs/_blob` — the same emitters `RichTextAttachment` models — and the\nbespoke six-key aggregate a chat upload line builds inline in\n`chats/lines/_upload.json.jbuilder`. Only the four keys both variants\nalways emit are `@required`; the rest identify their variant.",
31918
+ "properties": {
31919
+ "filename": {
31920
+ "type": "string",
31921
+ "description": "Original filename (both variants)."
30840
31922
  },
30841
- "bubble_up_url": {
31923
+ "content_type": {
30842
31924
  "type": "string",
30843
- "description": "URL of the Bubble Up record for this recording (BC5 addition). Optional\nhere because this is a polymorphic projection:\n`recordings/_recording.json.jbuilder` emits the key only when the caller\npasses `local_assigns[:bubbleupable]`, and `todolists/_todolist` is the\nonly partial that does. So a Todolist-shaped instance carries it and the\nother recording types do not."
31925
+ "description": "MIME type of the file (both variants)."
30844
31926
  },
30845
- "parent": {
30846
- "$ref": "#/components/schemas/RecordingParent"
31927
+ "byte_size": {
31928
+ "type": "integer",
31929
+ "description": "Size of the file in bytes (both variants).",
31930
+ "format": "int64"
30847
31931
  },
30848
- "bucket": {
30849
- "$ref": "#/components/schemas/RecordingBucket"
31932
+ "download_url": {
31933
+ "type": "string",
31934
+ "description": "Authenticated download URL for the file (both variants).",
31935
+ "x-basecamp-auth-routable-url": {}
30850
31936
  },
30851
- "creator": {
30852
- "$ref": "#/components/schemas/Person"
31937
+ "id": {
31938
+ "type": "integer",
31939
+ "description": "Attachment id (rich-text variant).",
31940
+ "format": "int64"
30853
31941
  },
30854
- "content": {
30855
- "type": [
30856
- "string",
30857
- "null"
30858
- ],
30859
- "description": "Always present, always null. `api/searches/show.json.jbuilder` renders the\nrecording's own partial and then unconditionally overwrites `content` with\n`nil` to strip the large HTML body out of the search payload. The key is\ntherefore guaranteed present on every result — required — and its value is\nguaranteed null. Read `plain_text_content` instead.",
30860
- "x-go-type": "string"
31942
+ "sgid": {
31943
+ "type": "string",
31944
+ "description": "Signed global id of the attachment (rich-text variant)."
30861
31945
  },
30862
- "description": {
30863
- "type": [
30864
- "string",
30865
- "null"
30866
- ],
30867
- "description": "Always present, always null — the description-attribute counterpart to\n`content`. Read `plain_text_description` instead.",
30868
- "x-go-type": "string"
31946
+ "previewable": {
31947
+ "type": "boolean",
31948
+ "description": "Whether the blob can be previewed (rich-text variant)."
30869
31949
  },
30870
- "plain_text_content": {
31950
+ "preview_url": {
30871
31951
  "type": "string",
30872
- "description": "A highlighted, truncated excerpt of the recording's content — **not** plain\ntext despite the name. `excerpt_and_highlight_matches` converts the rich\ntext with `to_plain_text`, escapes it with `html_escape_once`, then wraps\neach query match in `<mark class=\"circled-text\"><span></span>…</mark>` and\ntruncates the result to 300 characters. Treat it as an HTML fragment.\n\nOptional and non-nullable: emitted only when the underlying recordable\nresponds to `content`, so a result whose type has no content attribute\nomits the key entirely rather than sending null."
31952
+ "description": "Full-size preview URL (rich-text variant)."
30873
31953
  },
30874
- "plain_text_description": {
31954
+ "thumbnail_url": {
30875
31955
  "type": "string",
30876
- "description": "The description-attribute counterpart to `plain_text_content`, with the\nsame highlighting, escaping, and 300-character truncation. Optional and\nnon-nullable — omitted when the recordable has no description attribute."
31956
+ "description": "Thumbnail URL (rich-text variant)."
30877
31957
  },
30878
- "content_attachments": {
30879
- "type": "array",
30880
- "items": {
30881
- "$ref": "#/components/schemas/RichTextAttachment"
30882
- },
30883
- "description": "Rich-text companion arrays carried through the polymorphic search\nprojection. A given result is one recording type, so it carries only\nthe array matching its rich-text attribute (`content_attachments` for a\nComment/Message, `description_attachments` for a Todo); a webhook-sourced\nresult carries neither. Optional (no `@required`), non-nullable.\n\nSearch results additionally repeat this same array under a generic\n`attachments` key. It is a redundant projection, not a distinct\naggregate: `searches/show.json.jbuilder` emits\n`recording.downloadable_attachments`, which delegates to the recordable's\nsole `rich_text_content`, through the same `attachments/_attachment`\npartial that `recordings/_rich_text.json.jbuilder` uses to build the\ncompanion array. `RichText.rich_text_attribute` permits exactly one\nrich-text attribute per model, so the two keys always carry identical\nelements. Modeling `attachments` would duplicate the field, so it is\ndeliberately not modeled.",
30884
- "x-go-type-skip-optional-pointer": true
31958
+ "width": {
31959
+ "type": "integer",
31960
+ "description": "Pixel width (rich-text variant) — null for non-image blobs and may be\nfloat-spelled (`1024.0`); see `RichTextAttachment.width`.",
31961
+ "format": "int32",
31962
+ "nullable": true,
31963
+ "x-go-type": "types.FlexInt",
31964
+ "x-go-type-import": {
31965
+ "path": "github.com/basecamp/basecamp-sdk/go/pkg/types"
31966
+ }
30885
31967
  },
30886
- "description_attachments": {
30887
- "type": "array",
30888
- "items": {
30889
- "$ref": "#/components/schemas/RichTextAttachment"
30890
- },
30891
- "description": "See `content_attachments` — the description-attribute companion array.",
30892
- "x-go-type-skip-optional-pointer": true
31968
+ "height": {
31969
+ "type": "integer",
31970
+ "description": "See `width` (rich-text variant).",
31971
+ "format": "int32",
31972
+ "nullable": true,
31973
+ "x-go-type": "types.FlexInt",
31974
+ "x-go-type-import": {
31975
+ "path": "github.com/basecamp/basecamp-sdk/go/pkg/types"
31976
+ }
30893
31977
  },
30894
- "subject": {
30895
- "type": "string"
31978
+ "title": {
31979
+ "type": "string",
31980
+ "description": "Title of the attachment recording (chat upload-line variant)."
31981
+ },
31982
+ "url": {
31983
+ "type": "string",
31984
+ "description": "Browser preview URL of the blob (chat upload-line variant)."
30896
31985
  }
30897
31986
  },
30898
31987
  "required": [
30899
- "app_url",
30900
- "content",
30901
- "description",
30902
- "id",
30903
- "title",
30904
- "type",
30905
- "url"
31988
+ "byte_size",
31989
+ "content_type",
31990
+ "download_url",
31991
+ "filename"
30906
31992
  ]
30907
31993
  },
30908
31994
  "SearchType": {
@@ -30970,6 +32056,9 @@
30970
32056
  "SetClientVisibilityResponseContent": {
30971
32057
  "$ref": "#/components/schemas/Recording"
30972
32058
  },
32059
+ "SpotlightRecordingResponseContent": {
32060
+ "$ref": "#/components/schemas/Recording"
32061
+ },
30973
32062
  "StorageLimitErrorResponseContent": {
30974
32063
  "type": "object",
30975
32064
  "description": "The account has reached its file storage limit.\n\nRaised by ResourceLimits#ensure_account_can_upload_files ahead of any operation\nthat stores new bytes. No retry can satisfy it: the account needs more storage,\nso this maps to `limit_exceeded` rather than a retryable server error.",
@@ -31066,6 +32155,92 @@
31066
32155
  "updated_at"
31067
32156
  ]
31068
32157
  },
32158
+ "TemplateLibrary": {
32159
+ "type": "object",
32160
+ "properties": {
32161
+ "bucket": {
32162
+ "$ref": "#/components/schemas/RecordingBucket"
32163
+ },
32164
+ "todoset": {
32165
+ "$ref": "#/components/schemas/RecordingParent"
32166
+ },
32167
+ "todolists": {
32168
+ "type": "array",
32169
+ "items": {
32170
+ "$ref": "#/components/schemas/Todolist"
32171
+ }
32172
+ }
32173
+ },
32174
+ "required": [
32175
+ "bucket",
32176
+ "todolists",
32177
+ "todoset"
32178
+ ]
32179
+ },
32180
+ "TemplateLibraryConfirmationPerson": {
32181
+ "type": "object",
32182
+ "properties": {
32183
+ "id": {
32184
+ "type": "integer",
32185
+ "format": "int64"
32186
+ },
32187
+ "name": {
32188
+ "type": "string",
32189
+ "format": "password",
32190
+ "x-basecamp-sensitive": {
32191
+ "category": "pii",
32192
+ "redact": true
32193
+ }
32194
+ },
32195
+ "avatar_url": {
32196
+ "type": "string",
32197
+ "format": "password",
32198
+ "x-basecamp-sensitive": {
32199
+ "category": "pii",
32200
+ "redact": true
32201
+ }
32202
+ }
32203
+ },
32204
+ "required": [
32205
+ "avatar_url",
32206
+ "id",
32207
+ "name"
32208
+ ]
32209
+ },
32210
+ "TemplateLibraryCopy": {
32211
+ "type": "object",
32212
+ "properties": {
32213
+ "id": {
32214
+ "type": "integer",
32215
+ "format": "int64"
32216
+ },
32217
+ "status": {
32218
+ "type": "string",
32219
+ "description": "pending|processing|completed|failed"
32220
+ },
32221
+ "source_recording_id": {
32222
+ "type": "integer",
32223
+ "format": "int64"
32224
+ },
32225
+ "destination_parent_id": {
32226
+ "type": "integer",
32227
+ "format": "int64"
32228
+ },
32229
+ "url": {
32230
+ "type": "string"
32231
+ },
32232
+ "destination_todolist": {
32233
+ "$ref": "#/components/schemas/Todolist"
32234
+ }
32235
+ },
32236
+ "required": [
32237
+ "destination_parent_id",
32238
+ "id",
32239
+ "source_recording_id",
32240
+ "status",
32241
+ "url"
32242
+ ]
32243
+ },
31069
32244
  "TimelineAttachment": {
31070
32245
  "type": "object",
31071
32246
  "description": "A single timeline-event attachment. This is an optional-field superset over\ntwo wire variants — a full Upload recording (upload-kind recordings, rendered\nby BC3's uploads/_upload partial: the complete recording projection + rich-\ntext description + the upload body) and a rich-text attachment/blob partial\n(all other recordings) — so one element type decodes either. Every field is\noptional; a given instance populates only the fields of the variant it\nrepresents. The upload-recording variant enumerates the full documented\nprojection so no documented field is silently dropped on decode.",
@@ -31912,6 +33087,9 @@
31912
33087
  "status": {
31913
33088
  "type": "string"
31914
33089
  },
33090
+ "visible_to_clients": {
33091
+ "type": "boolean"
33092
+ },
31915
33093
  "created_at": {
31916
33094
  "type": "string",
31917
33095
  "x-go-type": "time.Time",
@@ -31929,15 +33107,12 @@
31929
33107
  "title": {
31930
33108
  "type": "string"
31931
33109
  },
31932
- "name": {
31933
- "type": "string"
31934
- },
31935
- "enabled": {
33110
+ "inherits_status": {
31936
33111
  "type": "boolean"
31937
33112
  },
31938
- "position": {
31939
- "type": "integer",
31940
- "format": "int32"
33113
+ "type": {
33114
+ "type": "string",
33115
+ "description": "The tool's recordable type, e.g. `Chat::Transcript`, `Todoset`, `Vault`."
31941
33116
  },
31942
33117
  "url": {
31943
33118
  "type": "string"
@@ -31945,17 +33120,45 @@
31945
33120
  "app_url": {
31946
33121
  "type": "string"
31947
33122
  },
33123
+ "bookmark_url": {
33124
+ "type": "string"
33125
+ },
33126
+ "subscription_url": {
33127
+ "type": "string",
33128
+ "description": "Absent for tool types that are not subscribable."
33129
+ },
33130
+ "position": {
33131
+ "type": "integer",
33132
+ "description": "Emitted only for a positioned recording. For a docked tool that makes an\nabsent position the disabled signal — disabling removes the tool from the\ndock without deleting it. Positioning is independent of dockedness, so\nthis does not generalize: a nested vault is not docked and is positioned.",
33133
+ "format": "int32"
33134
+ },
33135
+ "parent": {
33136
+ "$ref": "#/components/schemas/RecordingParent"
33137
+ },
31948
33138
  "bucket": {
31949
33139
  "$ref": "#/components/schemas/RecordingBucket"
33140
+ },
33141
+ "creator": {
33142
+ "$ref": "#/components/schemas/Person"
33143
+ },
33144
+ "name": {
33145
+ "type": "string",
33146
+ "description": "Not emitted by this projection. The dock array on a project\n(`DockItem$name`) carries the tool's slug; this key is absent from every\nGetTool/CreateTool/UpdateTool response."
33147
+ },
33148
+ "enabled": {
33149
+ "type": "boolean",
33150
+ "description": "Not emitted by this projection. The dock array on a project\n(`DockItem$enabled`) is the authoritative enabled flag; on a docked tool's\nown response, an absent `position` is the equivalent signal."
31950
33151
  }
31951
33152
  },
31952
33153
  "required": [
31953
33154
  "created_at",
31954
- "enabled",
33155
+ "creator",
31955
33156
  "id",
31956
- "name",
33157
+ "inherits_status",
31957
33158
  "title",
31958
- "updated_at"
33159
+ "type",
33160
+ "updated_at",
33161
+ "visible_to_clients"
31959
33162
  ]
31960
33163
  },
31961
33164
  "UnauthorizedErrorResponseContent": {