@37signals/basecamp 0.25.0 → 0.27.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 (67) hide show
  1. package/README.md +2 -0
  2. package/dist/client.d.ts +7 -2
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +7 -2
  5. package/dist/client.js.map +1 -1
  6. package/dist/generated/metadata.d.ts.map +1 -1
  7. package/dist/generated/metadata.js +72 -1
  8. package/dist/generated/metadata.js.map +1 -1
  9. package/dist/generated/metadata.ts +72 -1
  10. package/dist/generated/openapi-stripped.json +766 -20
  11. package/dist/generated/path-mapping.d.ts.map +1 -1
  12. package/dist/generated/path-mapping.js +6 -0
  13. package/dist/generated/path-mapping.js.map +1 -1
  14. package/dist/generated/path-mapping.ts +6 -0
  15. package/dist/generated/person-id-sites.d.ts.map +1 -1
  16. package/dist/generated/person-id-sites.js +12 -0
  17. package/dist/generated/person-id-sites.js.map +1 -1
  18. package/dist/generated/person-id-sites.ts +12 -0
  19. package/dist/generated/schema.d.ts +707 -40
  20. package/dist/generated/services/boosts.d.ts +20 -0
  21. package/dist/generated/services/boosts.d.ts.map +1 -1
  22. package/dist/generated/services/boosts.js +32 -0
  23. package/dist/generated/services/boosts.js.map +1 -1
  24. package/dist/generated/services/boosts.ts +45 -0
  25. package/dist/generated/services/cards.d.ts +3 -2
  26. package/dist/generated/services/cards.d.ts.map +1 -1
  27. package/dist/generated/services/cards.js +1 -1
  28. package/dist/generated/services/cards.js.map +1 -1
  29. package/dist/generated/services/cards.ts +3 -2
  30. package/dist/generated/services/embeds.d.ts +44 -0
  31. package/dist/generated/services/embeds.d.ts.map +1 -0
  32. package/dist/generated/services/embeds.js +67 -0
  33. package/dist/generated/services/embeds.js.map +1 -0
  34. package/dist/generated/services/embeds.ts +94 -0
  35. package/dist/generated/services/index.d.ts +2 -0
  36. package/dist/generated/services/index.d.ts.map +1 -1
  37. package/dist/generated/services/index.js +2 -0
  38. package/dist/generated/services/index.js.map +1 -1
  39. package/dist/generated/services/index.ts +2 -0
  40. package/dist/generated/services/pings.d.ts +59 -0
  41. package/dist/generated/services/pings.d.ts.map +1 -0
  42. package/dist/generated/services/pings.js +89 -0
  43. package/dist/generated/services/pings.js.map +1 -0
  44. package/dist/generated/services/pings.ts +125 -0
  45. package/dist/generated/services/wormholes.d.ts +3 -3
  46. package/dist/generated/services/wormholes.js +3 -3
  47. package/dist/generated/services/wormholes.ts +3 -3
  48. package/dist/index.d.ts +3 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +3 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/services/recordings-extensions.d.ts +8 -7
  53. package/dist/services/recordings-extensions.d.ts.map +1 -1
  54. package/dist/services/recordings-extensions.js +63 -18
  55. package/dist/services/recordings-extensions.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/generated/metadata.ts +72 -1
  58. package/src/generated/openapi-stripped.json +766 -20
  59. package/src/generated/path-mapping.ts +6 -0
  60. package/src/generated/person-id-sites.ts +12 -0
  61. package/src/generated/schema.d.ts +707 -40
  62. package/src/generated/services/boosts.ts +45 -0
  63. package/src/generated/services/cards.ts +3 -2
  64. package/src/generated/services/embeds.ts +94 -0
  65. package/src/generated/services/index.ts +2 -0
  66. package/src/generated/services/pings.ts +125 -0
  67. package/src/generated/services/wormholes.ts +3 -3
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Basecamp",
5
- "version": "2026-09-15",
5
+ "version": "2026-10-19",
6
6
  "description": "Basecamp API",
7
7
  "contact": {
8
8
  "name": "Basecamp",
@@ -597,6 +597,101 @@
597
597
  503
598
598
  ]
599
599
  }
600
+ },
601
+ "put": {
602
+ "description": "Replace the content of a boost. Only the boost's creator may update it: an\nadmin updating someone else's boost gets 403 Forbidden, as does any caller\nwhen comments are closed on the boosted recording. A boost on a deleted\nrecording, or in another account, answers 404. A missing or invalid\n`content` answers a bodiless 422. There is no bucket-scoped route.",
603
+ "operationId": "UpdateBoost",
604
+ "requestBody": {
605
+ "content": {
606
+ "application/json": {
607
+ "schema": {
608
+ "$ref": "#/components/schemas/UpdateBoostRequestContent"
609
+ }
610
+ }
611
+ },
612
+ "required": true
613
+ },
614
+ "parameters": [
615
+ {
616
+ "name": "boostId",
617
+ "in": "path",
618
+ "schema": {
619
+ "type": "integer",
620
+ "format": "int64"
621
+ },
622
+ "required": true
623
+ }
624
+ ],
625
+ "responses": {
626
+ "200": {
627
+ "description": "UpdateBoost 200 response",
628
+ "content": {
629
+ "application/json": {
630
+ "schema": {
631
+ "$ref": "#/components/schemas/UpdateBoostResponseContent"
632
+ }
633
+ }
634
+ }
635
+ },
636
+ "401": {
637
+ "description": "UnauthorizedError 401 response",
638
+ "content": {
639
+ "application/json": {
640
+ "schema": {
641
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
642
+ }
643
+ }
644
+ }
645
+ },
646
+ "403": {
647
+ "description": "ForbiddenError 403 response",
648
+ "content": {
649
+ "application/json": {
650
+ "schema": {
651
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
652
+ }
653
+ }
654
+ }
655
+ },
656
+ "404": {
657
+ "description": "NotFoundError 404 response",
658
+ "content": {
659
+ "application/json": {
660
+ "schema": {
661
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
662
+ }
663
+ }
664
+ }
665
+ },
666
+ "422": {
667
+ "description": "BareValidationError 422 response"
668
+ },
669
+ "500": {
670
+ "description": "InternalServerError 500 response",
671
+ "content": {
672
+ "application/json": {
673
+ "schema": {
674
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
675
+ }
676
+ }
677
+ }
678
+ }
679
+ },
680
+ "tags": [
681
+ "Boosts"
682
+ ],
683
+ "x-basecamp-idempotent": {
684
+ "natural": true
685
+ },
686
+ "x-basecamp-retry": {
687
+ "maxAttempts": 3,
688
+ "baseDelayMs": 1000,
689
+ "backoff": "exponential",
690
+ "retryOn": [
691
+ 429,
692
+ 503
693
+ ]
694
+ }
600
695
  }
601
696
  },
602
697
  "/buckets/{bucketId}/card_tables/columns/{columnId}/color.json": {
@@ -928,7 +1023,7 @@
928
1023
  },
929
1024
  "/buckets/{bucketId}/card_tables/wormholes/{wormholeId}": {
930
1025
  "delete": {
931
- "description": "Delete a wormhole",
1026
+ "description": "Permanently delete a card table's wormhole, the shortcut that sends cards to another card table.\n\nIt does not go to the trash and cannot be restored.",
932
1027
  "operationId": "DeleteWormhole",
933
1028
  "parameters": [
934
1029
  {
@@ -1022,7 +1117,7 @@
1022
1117
  }
1023
1118
  },
1024
1119
  "put": {
1025
- "description": "Update a wormhole's destination column",
1120
+ "description": "Point a card table's wormhole, its shortcut for sending cards to another card table, at a different destination column",
1026
1121
  "operationId": "UpdateWormhole",
1027
1122
  "requestBody": {
1028
1123
  "content": {
@@ -1125,7 +1220,7 @@
1125
1220
  },
1126
1221
  "/buckets/{bucketId}/card_tables/{cardTableId}/wormholes.json": {
1127
1222
  "post": {
1128
- "description": "Create a wormhole linking this card table to a column on another card table.\n\nA wormhole is the only mechanism for moving a card to a different project: its\nid is a valid `column_id` for MoveCard, teleporting the card across projects.\n`destinationRecordingId` is the id of a column on another accessible card table.",
1223
+ "description": "Add a wormhole to a card table: a standing shortcut that sends cards to a column on another card table, usually in another project.\n\nA wormhole is the only mechanism for moving a card to a different project: its\nid is a valid `column_id` for MoveCard, teleporting the card there. It is not\nneeded for a one-off move within a project; MoveCard goes to any column there\ndirectly. A card table holds at most four wormholes.\n`destinationRecordingId` is the id of a column on another accessible card table.",
1129
1224
  "operationId": "CreateWormhole",
1130
1225
  "requestBody": {
1131
1226
  "content": {
@@ -3902,7 +3997,7 @@
3902
3997
  },
3903
3998
  "/card_tables/cards/{cardId}/moves.json": {
3904
3999
  "post": {
3905
- "description": "Move a card to a different column",
4000
+ "description": "Move a card to any column in its project, including on another card table there, or through a linked wormhole to another project (queued: the card gets a new id, and position, if sent, must still be positive but doesn't set placement).\n\n`column_id` may be any column in the card's project, including a column on\nanother card table in the same project; the card moves there directly, with\nno wormhole needed. To send the card to another project, pass the id of a\nlinked wormhole (`linked: true`) the card table already has, listed in\nGetCardTable's `wormholes`; an unlinked or unreachable one returns not found.\nA move through a wormhole is queued: the card arrives under a new id and its\nold id then returns not found. `position` does not control where it lands,\nbut if supplied it must still be a positive integer.",
3906
4001
  "operationId": "MoveCard",
3907
4002
  "requestBody": {
3908
4003
  "content": {
@@ -6930,6 +7025,159 @@
6930
7025
  }
6931
7026
  }
6932
7027
  },
7028
+ "/circles.json": {
7029
+ "get": {
7030
+ "description": "Get Pings: the caller's active Pings that have a line in them, most\nrecently updated first, in one unpaginated response. Archived Pings, and\nPings no one has posted in yet, are not listed; GetPing reads either by id.\n\nAnswers `403 Forbidden` (`{\"error\": \"Pings have been disabled\"}`) when the\ncaller cannot ping.",
7031
+ "operationId": "ListPings",
7032
+ "parameters": [],
7033
+ "responses": {
7034
+ "200": {
7035
+ "description": "ListPings 200 response",
7036
+ "content": {
7037
+ "application/json": {
7038
+ "schema": {
7039
+ "$ref": "#/components/schemas/ListPingsResponseContent"
7040
+ }
7041
+ }
7042
+ }
7043
+ },
7044
+ "401": {
7045
+ "description": "UnauthorizedError 401 response",
7046
+ "content": {
7047
+ "application/json": {
7048
+ "schema": {
7049
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
7050
+ }
7051
+ }
7052
+ }
7053
+ },
7054
+ "403": {
7055
+ "description": "ForbiddenError 403 response",
7056
+ "content": {
7057
+ "application/json": {
7058
+ "schema": {
7059
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
7060
+ }
7061
+ }
7062
+ }
7063
+ },
7064
+ "429": {
7065
+ "description": "RateLimitError 429 response",
7066
+ "content": {
7067
+ "application/json": {
7068
+ "schema": {
7069
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
7070
+ }
7071
+ }
7072
+ }
7073
+ },
7074
+ "500": {
7075
+ "description": "InternalServerError 500 response",
7076
+ "content": {
7077
+ "application/json": {
7078
+ "schema": {
7079
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
7080
+ }
7081
+ }
7082
+ }
7083
+ }
7084
+ },
7085
+ "tags": [
7086
+ "Pings"
7087
+ ],
7088
+ "x-basecamp-retry": {
7089
+ "maxAttempts": 3,
7090
+ "baseDelayMs": 1000,
7091
+ "backoff": "exponential",
7092
+ "retryOn": [
7093
+ 429,
7094
+ 503
7095
+ ]
7096
+ }
7097
+ },
7098
+ "post": {
7099
+ "description": "Create a Ping with people, or return the one that already holds exactly\nthem.\n\nThe caller is always included. Without `new_group`, an existing active\nPing with exactly these people (the oldest, if several) is returned rather\nthan a second one created; with `new_group: true` and more than one other\nperson, a new group is created every time. Either way the answer is\n`201 Created` with the Ping.\n\nAnswers `403 Forbidden` (`{\"error\": \"Pings have been disabled\"}`) when the\ncaller cannot ping, and a bodiless `422` when nobody in the Ping would be a\nperson who can ping.",
7100
+ "operationId": "CreatePing",
7101
+ "requestBody": {
7102
+ "content": {
7103
+ "application/json": {
7104
+ "schema": {
7105
+ "$ref": "#/components/schemas/CreatePingRequestContent"
7106
+ }
7107
+ }
7108
+ },
7109
+ "required": true
7110
+ },
7111
+ "parameters": [],
7112
+ "responses": {
7113
+ "201": {
7114
+ "description": "CreatePing 201 response",
7115
+ "content": {
7116
+ "application/json": {
7117
+ "schema": {
7118
+ "$ref": "#/components/schemas/CreatePingResponseContent"
7119
+ }
7120
+ }
7121
+ }
7122
+ },
7123
+ "401": {
7124
+ "description": "UnauthorizedError 401 response",
7125
+ "content": {
7126
+ "application/json": {
7127
+ "schema": {
7128
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
7129
+ }
7130
+ }
7131
+ }
7132
+ },
7133
+ "403": {
7134
+ "description": "ForbiddenError 403 response",
7135
+ "content": {
7136
+ "application/json": {
7137
+ "schema": {
7138
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
7139
+ }
7140
+ }
7141
+ }
7142
+ },
7143
+ "422": {
7144
+ "description": "BareValidationError 422 response"
7145
+ },
7146
+ "429": {
7147
+ "description": "RateLimitError 429 response",
7148
+ "content": {
7149
+ "application/json": {
7150
+ "schema": {
7151
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
7152
+ }
7153
+ }
7154
+ }
7155
+ },
7156
+ "500": {
7157
+ "description": "InternalServerError 500 response",
7158
+ "content": {
7159
+ "application/json": {
7160
+ "schema": {
7161
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
7162
+ }
7163
+ }
7164
+ }
7165
+ }
7166
+ },
7167
+ "tags": [
7168
+ "Pings"
7169
+ ],
7170
+ "x-basecamp-retry": {
7171
+ "maxAttempts": 2,
7172
+ "baseDelayMs": 1000,
7173
+ "backoff": "exponential",
7174
+ "retryOn": [
7175
+ 429,
7176
+ 503
7177
+ ]
7178
+ }
7179
+ }
7180
+ },
6933
7181
  "/circles/people.json": {
6934
7182
  "get": {
6935
7183
  "description": "List all account users who can be pinged\n\n**Pagination**: Uses Link header (RFC5988). Follow the `next` rel URL\nto fetch additional pages. X-Total-Count header provides total count.",
@@ -7006,16 +7254,18 @@
7006
7254
  }
7007
7255
  }
7008
7256
  },
7009
- "/client/approvals/{approvalId}": {
7257
+ "/circles/{pingId}": {
7010
7258
  "get": {
7011
- "description": "Get a single client approval by id",
7012
- "operationId": "GetClientApproval",
7259
+ "description": "Get a Ping by its id, which is its bucket's id.\n\nAnswers `403 Forbidden` (`{\"error\": \"Pings have been disabled\"}`) when the\ncaller cannot ping, and `404 Not Found` for a bucket that is not a Ping the\ncaller is in.",
7260
+ "operationId": "GetPing",
7013
7261
  "parameters": [
7014
7262
  {
7015
- "name": "approvalId",
7263
+ "name": "pingId",
7016
7264
  "in": "path",
7265
+ "description": "A Ping's id is the id of the bucket that holds it (the bucket type is\n\"Circle\", which is also the wire path), so the `bucket.id` a Ping's\nrecordings carry is the id GetPing takes.",
7017
7266
  "schema": {
7018
7267
  "type": "integer",
7268
+ "description": "A Ping's id is the id of the bucket that holds it (the bucket type is\n\"Circle\", which is also the wire path), so the `bucket.id` a Ping's\nrecordings carry is the id GetPing takes.",
7019
7269
  "format": "int64"
7020
7270
  },
7021
7271
  "required": true
@@ -7023,11 +7273,11 @@
7023
7273
  ],
7024
7274
  "responses": {
7025
7275
  "200": {
7026
- "description": "GetClientApproval 200 response",
7276
+ "description": "GetPing 200 response",
7027
7277
  "content": {
7028
7278
  "application/json": {
7029
7279
  "schema": {
7030
- "$ref": "#/components/schemas/GetClientApprovalResponseContent"
7280
+ "$ref": "#/components/schemas/GetPingResponseContent"
7031
7281
  }
7032
7282
  }
7033
7283
  }
@@ -7062,6 +7312,16 @@
7062
7312
  }
7063
7313
  }
7064
7314
  },
7315
+ "429": {
7316
+ "description": "RateLimitError 429 response",
7317
+ "content": {
7318
+ "application/json": {
7319
+ "schema": {
7320
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
7321
+ }
7322
+ }
7323
+ }
7324
+ },
7065
7325
  "500": {
7066
7326
  "description": "InternalServerError 500 response",
7067
7327
  "content": {
@@ -7074,7 +7334,7 @@
7074
7334
  }
7075
7335
  },
7076
7336
  "tags": [
7077
- "ClientFeatures"
7337
+ "Pings"
7078
7338
  ],
7079
7339
  "x-basecamp-retry": {
7080
7340
  "maxAttempts": 3,
@@ -7087,13 +7347,13 @@
7087
7347
  }
7088
7348
  }
7089
7349
  },
7090
- "/client/correspondences/{correspondenceId}": {
7350
+ "/client/approvals/{approvalId}": {
7091
7351
  "get": {
7092
- "description": "Get a single client correspondence by id",
7093
- "operationId": "GetClientCorrespondence",
7352
+ "description": "Get a single client approval by id",
7353
+ "operationId": "GetClientApproval",
7094
7354
  "parameters": [
7095
7355
  {
7096
- "name": "correspondenceId",
7356
+ "name": "approvalId",
7097
7357
  "in": "path",
7098
7358
  "schema": {
7099
7359
  "type": "integer",
@@ -7104,11 +7364,92 @@
7104
7364
  ],
7105
7365
  "responses": {
7106
7366
  "200": {
7107
- "description": "GetClientCorrespondence 200 response",
7367
+ "description": "GetClientApproval 200 response",
7108
7368
  "content": {
7109
7369
  "application/json": {
7110
7370
  "schema": {
7111
- "$ref": "#/components/schemas/GetClientCorrespondenceResponseContent"
7371
+ "$ref": "#/components/schemas/GetClientApprovalResponseContent"
7372
+ }
7373
+ }
7374
+ }
7375
+ },
7376
+ "401": {
7377
+ "description": "UnauthorizedError 401 response",
7378
+ "content": {
7379
+ "application/json": {
7380
+ "schema": {
7381
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
7382
+ }
7383
+ }
7384
+ }
7385
+ },
7386
+ "403": {
7387
+ "description": "ForbiddenError 403 response",
7388
+ "content": {
7389
+ "application/json": {
7390
+ "schema": {
7391
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
7392
+ }
7393
+ }
7394
+ }
7395
+ },
7396
+ "404": {
7397
+ "description": "NotFoundError 404 response",
7398
+ "content": {
7399
+ "application/json": {
7400
+ "schema": {
7401
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
7402
+ }
7403
+ }
7404
+ }
7405
+ },
7406
+ "500": {
7407
+ "description": "InternalServerError 500 response",
7408
+ "content": {
7409
+ "application/json": {
7410
+ "schema": {
7411
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
7412
+ }
7413
+ }
7414
+ }
7415
+ }
7416
+ },
7417
+ "tags": [
7418
+ "ClientFeatures"
7419
+ ],
7420
+ "x-basecamp-retry": {
7421
+ "maxAttempts": 3,
7422
+ "baseDelayMs": 1000,
7423
+ "backoff": "exponential",
7424
+ "retryOn": [
7425
+ 429,
7426
+ 503
7427
+ ]
7428
+ }
7429
+ }
7430
+ },
7431
+ "/client/correspondences/{correspondenceId}": {
7432
+ "get": {
7433
+ "description": "Get a single client correspondence by id",
7434
+ "operationId": "GetClientCorrespondence",
7435
+ "parameters": [
7436
+ {
7437
+ "name": "correspondenceId",
7438
+ "in": "path",
7439
+ "schema": {
7440
+ "type": "integer",
7441
+ "format": "int64"
7442
+ },
7443
+ "required": true
7444
+ }
7445
+ ],
7446
+ "responses": {
7447
+ "200": {
7448
+ "description": "GetClientCorrespondence 200 response",
7449
+ "content": {
7450
+ "application/json": {
7451
+ "schema": {
7452
+ "$ref": "#/components/schemas/GetClientCorrespondenceResponseContent"
7112
7453
  }
7113
7454
  }
7114
7455
  }
@@ -8069,6 +8410,201 @@
8069
8410
  }
8070
8411
  }
8071
8412
  },
8413
+ "/embeds.json": {
8414
+ "post": {
8415
+ "description": "Create an embed for a URL, or return the one Basecamp already has for it.\n\nAnswers `200 OK` with the embed either way. A new embed's status is\n`pending`; get it about once a second until its status is `active` or\n`not_found`.\n\nAnswers `422` (`{\"error\": \"Basecamp can't embed this URL. Link to it\ninstead.\"}`) for a URL no provider embeds. Past 100 new embeds in an hour,\ncreating another answers `429` (`{\"error\": \"Too many new embeds. Try again\nlater.\"}`) with no Retry-After; asking for an embed Basecamp already has\nstill works. That 429 is a verdict for the rest of the hour, not\nthrottling, so this operation declares `retryOn: [503]` and the 429\nsurfaces on the first attempt.",
8416
+ "operationId": "CreateEmbed",
8417
+ "requestBody": {
8418
+ "content": {
8419
+ "application/json": {
8420
+ "schema": {
8421
+ "$ref": "#/components/schemas/CreateEmbedRequestContent"
8422
+ }
8423
+ }
8424
+ },
8425
+ "required": true
8426
+ },
8427
+ "parameters": [],
8428
+ "responses": {
8429
+ "200": {
8430
+ "description": "CreateEmbed 200 response",
8431
+ "content": {
8432
+ "application/json": {
8433
+ "schema": {
8434
+ "$ref": "#/components/schemas/CreateEmbedResponseContent"
8435
+ }
8436
+ }
8437
+ }
8438
+ },
8439
+ "400": {
8440
+ "description": "BadRequestError 400 response",
8441
+ "content": {
8442
+ "application/json": {
8443
+ "schema": {
8444
+ "$ref": "#/components/schemas/BadRequestErrorResponseContent"
8445
+ }
8446
+ }
8447
+ }
8448
+ },
8449
+ "401": {
8450
+ "description": "UnauthorizedError 401 response",
8451
+ "content": {
8452
+ "application/json": {
8453
+ "schema": {
8454
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
8455
+ }
8456
+ }
8457
+ }
8458
+ },
8459
+ "403": {
8460
+ "description": "ForbiddenError 403 response",
8461
+ "content": {
8462
+ "application/json": {
8463
+ "schema": {
8464
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
8465
+ }
8466
+ }
8467
+ }
8468
+ },
8469
+ "422": {
8470
+ "description": "ValidationError 422 response",
8471
+ "content": {
8472
+ "application/json": {
8473
+ "schema": {
8474
+ "$ref": "#/components/schemas/ValidationErrorResponseContent"
8475
+ }
8476
+ }
8477
+ }
8478
+ },
8479
+ "429": {
8480
+ "description": "RateLimitError 429 response",
8481
+ "content": {
8482
+ "application/json": {
8483
+ "schema": {
8484
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
8485
+ }
8486
+ }
8487
+ }
8488
+ },
8489
+ "500": {
8490
+ "description": "InternalServerError 500 response",
8491
+ "content": {
8492
+ "application/json": {
8493
+ "schema": {
8494
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
8495
+ }
8496
+ }
8497
+ }
8498
+ }
8499
+ },
8500
+ "tags": [
8501
+ "Embeds"
8502
+ ],
8503
+ "x-basecamp-idempotent": {
8504
+ "natural": true
8505
+ },
8506
+ "x-basecamp-retry": {
8507
+ "maxAttempts": 3,
8508
+ "baseDelayMs": 1000,
8509
+ "backoff": "exponential",
8510
+ "retryOn": [
8511
+ 503
8512
+ ]
8513
+ }
8514
+ }
8515
+ },
8516
+ "/embeds/{sgid}": {
8517
+ "get": {
8518
+ "description": "Get an embed by its sgid.\n\nAnswers `404 Not Found` for an sgid that does not name an embed.",
8519
+ "operationId": "GetEmbed",
8520
+ "parameters": [
8521
+ {
8522
+ "name": "sgid",
8523
+ "in": "path",
8524
+ "description": "An embed's signed global id, an opaque string.",
8525
+ "schema": {
8526
+ "type": "string",
8527
+ "description": "An embed's signed global id, an opaque string."
8528
+ },
8529
+ "required": true
8530
+ }
8531
+ ],
8532
+ "responses": {
8533
+ "200": {
8534
+ "description": "GetEmbed 200 response",
8535
+ "content": {
8536
+ "application/json": {
8537
+ "schema": {
8538
+ "$ref": "#/components/schemas/GetEmbedResponseContent"
8539
+ }
8540
+ }
8541
+ }
8542
+ },
8543
+ "401": {
8544
+ "description": "UnauthorizedError 401 response",
8545
+ "content": {
8546
+ "application/json": {
8547
+ "schema": {
8548
+ "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
8549
+ }
8550
+ }
8551
+ }
8552
+ },
8553
+ "403": {
8554
+ "description": "ForbiddenError 403 response",
8555
+ "content": {
8556
+ "application/json": {
8557
+ "schema": {
8558
+ "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
8559
+ }
8560
+ }
8561
+ }
8562
+ },
8563
+ "404": {
8564
+ "description": "NotFoundError 404 response",
8565
+ "content": {
8566
+ "application/json": {
8567
+ "schema": {
8568
+ "$ref": "#/components/schemas/NotFoundErrorResponseContent"
8569
+ }
8570
+ }
8571
+ }
8572
+ },
8573
+ "429": {
8574
+ "description": "RateLimitError 429 response",
8575
+ "content": {
8576
+ "application/json": {
8577
+ "schema": {
8578
+ "$ref": "#/components/schemas/RateLimitErrorResponseContent"
8579
+ }
8580
+ }
8581
+ }
8582
+ },
8583
+ "500": {
8584
+ "description": "InternalServerError 500 response",
8585
+ "content": {
8586
+ "application/json": {
8587
+ "schema": {
8588
+ "$ref": "#/components/schemas/InternalServerErrorResponseContent"
8589
+ }
8590
+ }
8591
+ }
8592
+ }
8593
+ },
8594
+ "tags": [
8595
+ "Embeds"
8596
+ ],
8597
+ "x-basecamp-retry": {
8598
+ "maxAttempts": 3,
8599
+ "baseDelayMs": 1000,
8600
+ "backoff": "exponential",
8601
+ "retryOn": [
8602
+ 429,
8603
+ 503
8604
+ ]
8605
+ }
8606
+ }
8607
+ },
8072
8608
  "/events.json": {
8073
8609
  "get": {
8074
8610
  "description": "Poll the account event feed for events after a position (oldest first, strict event-id order, up to 100 per page).\n\n**Entry.** With neither `since` nor `position` the feed begins at the present\n(equivalent to `since=now`). `since=<event id>` starts after that id and\n`since=0` replays all served history back to the feed's epoch; `since` is a\nsigned 64-bit integer written in decimal, or the literal `now`. `position`\nresumes from a token a previous page issued — signed, opaque, bound to the\naccount and the filter set.\n\n**Pagination**: cursor style — the body envelope, not the Link header.\n`position` is the durable cursor (persist it only after processing the\npage's events); `next` is an absolute continuation URL present only while\nthis walk has more to serve. One call answers one page; no generator emits\na walk for the cursor style — see the section note.\n\n**Errors.** 400 (FeedRequestError) for a malformed position (resume with\n`since=`) or a malformed filter (fix the filters; a position reset will not\nhelp), told apart by its optional `reason` and undifferentiated when `reason`\nis absent. 409 (FeedFilterMismatchError) when the position was minted for a\ndifferent filter set. 410 (FeedPositionGoneError) when the position predates\nthe feed's epoch; its `resume` re-enters at the epoch.",
@@ -13734,7 +14270,7 @@
13734
14270
  },
13735
14271
  "/projects/{projectId}/people/client_users.json": {
13736
14272
  "put": {
13737
- "description": "Update project client access (grant/revoke/create client users)\n\nThe client-side counterpart to UpdateProjectAccess: `grant` adds existing\nclient users by id, `revoke` removes client users, and `create` invites\nbrand-new clients by email (`name` optional, defaulting to the address).\nOnly client users are eligible — a `grant` id belonging to a team member is\nrejected (omitted from `granted`) rather than cross-graded, and `revoke` never removes a team\nmember. The response mirrors UpdateProjectAccess: `granted` and `revoked`\npeople, each with `client: true`.\n\nRequires clients to be enabled on the project (EnableProjectClients);\notherwise 403. Invitations are all-or-nothing: an invalid `create` row\n(including one with no email address) answers 422 with the rejected\naddresses and nobody is invited; new addresses that would exceed the\naccount's user limit answer 429 and nobody is invited. Addresses already on\nthe account take no seat and a repeated address counts once.\n\nThe seat-limit 429 is a verdict, not throttling: it carries no Retry-After\nand re-asking cannot change the answer, so this operation declares\n`retryOn: [503]` and a 429 surfaces on the first attempt (status-mapped to\n`rate_limit`, since the wire status is the only signal). Every other\noperation retries on 429.",
14273
+ "description": "Update project client access (grant/revoke/create client users)\n\nThe client-side counterpart to UpdateProjectAccess: `grant` adds existing\nclient users by id, `revoke` removes client users, and `create` invites\nbrand-new clients by email (`name` optional, defaulting to the address).\nOnly client users are eligible — a `grant` id belonging to a team member is\nrejected (omitted from `granted`) rather than cross-graded, and `revoke` never removes a team\nmember. The response mirrors UpdateProjectAccess: `granted` and `revoked`\npeople, each with `client: true`.\n\nRequires clients to be enabled on the project (EnableProjectClients);\notherwise 403. Invitations are all-or-nothing: an invalid `create` row\n(including one with no email address) answers 422 with the rejected\naddresses and nobody is invited; new addresses that would exceed the\naccount's user limit answer 429 and nobody is invited. Addresses already on\nthe account take no seat and a repeated address counts once.\n\nThe seat-limit 429 is a verdict, not throttling: it carries no Retry-After\nand re-asking cannot change the answer, so this operation declares\n`retryOn: [503]` and a 429 surfaces on the first attempt (status-mapped to\n`rate_limit`, since the wire status is the only signal). Every other\noperation but CreateEmbed retries on 429.",
13738
14274
  "operationId": "UpdateProjectClientAccess",
13739
14275
  "requestBody": {
13740
14276
  "content": {
@@ -27727,6 +28263,7 @@
27727
28263
  "items": {
27728
28264
  "$ref": "#/components/schemas/Wormhole"
27729
28265
  },
28266
+ "description": "The card table's wormholes: shortcuts for sending cards to other card\ntables, usually in other projects. Only wormholes whose destination the\nrequester can reach are listed; the rest are omitted, not shown unlinked.\nNot needed to move a card within its project; MoveCard reaches any column\nthere directly.",
27730
28267
  "x-go-type-skip-optional-pointer": true
27731
28268
  }
27732
28269
  },
@@ -28689,6 +29226,21 @@
28689
29226
  "CreateDocumentResponseContent": {
28690
29227
  "$ref": "#/components/schemas/Document"
28691
29228
  },
29229
+ "CreateEmbedRequestContent": {
29230
+ "type": "object",
29231
+ "properties": {
29232
+ "url": {
29233
+ "type": "string",
29234
+ "description": "The URL of the content to embed."
29235
+ }
29236
+ },
29237
+ "required": [
29238
+ "url"
29239
+ ]
29240
+ },
29241
+ "CreateEmbedResponseContent": {
29242
+ "$ref": "#/components/schemas/Embed"
29243
+ },
28692
29244
  "CreateEventBoostRequestContent": {
28693
29245
  "type": "object",
28694
29246
  "properties": {
@@ -28886,6 +29438,29 @@
28886
29438
  "name"
28887
29439
  ]
28888
29440
  },
29441
+ "CreatePingRequestContent": {
29442
+ "type": "object",
29443
+ "properties": {
29444
+ "user_ids": {
29445
+ "type": "array",
29446
+ "items": {
29447
+ "type": "integer",
29448
+ "format": "int64"
29449
+ },
29450
+ "description": "Person ids of the people to ping. People the caller cannot reach are\ndropped."
29451
+ },
29452
+ "new_group": {
29453
+ "type": "boolean",
29454
+ "description": "Create a new group even when a Ping with exactly these people exists."
29455
+ }
29456
+ },
29457
+ "required": [
29458
+ "user_ids"
29459
+ ]
29460
+ },
29461
+ "CreatePingResponseContent": {
29462
+ "$ref": "#/components/schemas/Ping"
29463
+ },
28889
29464
  "CreateProjectFromTemplateRequestContent": {
28890
29465
  "type": "object",
28891
29466
  "properties": {
@@ -29761,6 +30336,45 @@
29761
30336
  "title"
29762
30337
  ]
29763
30338
  },
30339
+ "Embed": {
30340
+ "type": "object",
30341
+ "description": "Content from another site, such as a YouTube video, that rich text shows\ninline. Put it in rich text as `<bc-attachment sgid=\"SGID\"></bc-attachment>`\nonce its status is `active`; an embed that is not active shows as a link to\nits URL.",
30342
+ "properties": {
30343
+ "url": {
30344
+ "type": "string"
30345
+ },
30346
+ "status": {
30347
+ "type": "string",
30348
+ "description": "`pending` while Basecamp asks the provider about the URL, then `active`,\nor `not_found` when the provider has nothing to embed."
30349
+ },
30350
+ "sgid": {
30351
+ "type": "string",
30352
+ "description": "An embed's signed global id, an opaque string."
30353
+ },
30354
+ "type": {
30355
+ "type": "string",
30356
+ "description": "The provider's oEmbed type, such as `video`, `rich` or `photo`. Present\nonly when the embed is active."
30357
+ },
30358
+ "content_type": {
30359
+ "type": "string",
30360
+ "description": "Such as `embed/youtube-video`. Present only when the embed is active."
30361
+ },
30362
+ "image_url": {
30363
+ "type": "string",
30364
+ "description": "Present only when the embed is active, and null when the provider gave no\nimage.",
30365
+ "nullable": true
30366
+ },
30367
+ "content": {
30368
+ "type": "string",
30369
+ "description": "The provider's HTML, sanitized. Present only when the embed is active and\nthe provider gave HTML; may be empty."
30370
+ }
30371
+ },
30372
+ "required": [
30373
+ "sgid",
30374
+ "status",
30375
+ "url"
30376
+ ]
30377
+ },
29764
30378
  "EnableCardColumnOnHoldResponseContent": {
29765
30379
  "$ref": "#/components/schemas/CardColumn"
29766
30380
  },
@@ -30864,6 +31478,9 @@
30864
31478
  "GetDocumentResponseContent": {
30865
31479
  "$ref": "#/components/schemas/Document"
30866
31480
  },
31481
+ "GetEmbedResponseContent": {
31482
+ "$ref": "#/components/schemas/Embed"
31483
+ },
30867
31484
  "GetEverythingCheckinsResponseContent": {
30868
31485
  "type": "array",
30869
31486
  "items": {
@@ -31144,6 +31761,9 @@
31144
31761
  "GetPersonResponseContent": {
31145
31762
  "$ref": "#/components/schemas/Person"
31146
31763
  },
31764
+ "GetPingResponseContent": {
31765
+ "$ref": "#/components/schemas/Ping"
31766
+ },
31147
31767
  "GetProgressReportResponseContent": {
31148
31768
  "type": "array",
31149
31769
  "items": {
@@ -31785,6 +32405,12 @@
31785
32405
  "$ref": "#/components/schemas/Person"
31786
32406
  }
31787
32407
  },
32408
+ "ListPingsResponseContent": {
32409
+ "type": "array",
32410
+ "items": {
32411
+ "$ref": "#/components/schemas/Ping"
32412
+ }
32413
+ },
31788
32414
  "ListProjectPeopleResponseContent": {
31789
32415
  "type": "array",
31790
32416
  "items": {
@@ -32155,6 +32781,7 @@
32155
32781
  "properties": {
32156
32782
  "column_id": {
32157
32783
  "type": "integer",
32784
+ "description": "Any column in the card's project, on this or another card table, or a\nwormhole id to send the card to another project.",
32158
32785
  "format": "int64"
32159
32786
  },
32160
32787
  "position": {
@@ -32780,6 +33407,110 @@
32780
33407
  "name"
32781
33408
  ]
32782
33409
  },
33410
+ "Ping": {
33411
+ "type": "object",
33412
+ "description": "A Ping: a private conversation between people, held in its own bucket with\none Campfire. The Campfire's lines are read and written through the\nCampfires service; `room_url` and `links.lines` name it.",
33413
+ "properties": {
33414
+ "id": {
33415
+ "type": "integer",
33416
+ "description": "The Ping's id, which is also its bucket's id.",
33417
+ "format": "int64"
33418
+ },
33419
+ "created_at": {
33420
+ "type": "string",
33421
+ "x-go-type": "time.Time",
33422
+ "x-go-type-import": {
33423
+ "path": "time"
33424
+ }
33425
+ },
33426
+ "updated_at": {
33427
+ "type": "string",
33428
+ "x-go-type": "time.Time",
33429
+ "x-go-type-import": {
33430
+ "path": "time"
33431
+ }
33432
+ },
33433
+ "title": {
33434
+ "type": "string",
33435
+ "description": "The Ping's name for the caller: its given name when `named`, else the\nnames of the other people in it."
33436
+ },
33437
+ "named": {
33438
+ "type": "boolean",
33439
+ "description": "Whether someone gave the Ping a name."
33440
+ },
33441
+ "image_url": {
33442
+ "type": "string",
33443
+ "description": "The Ping's own image, present only when one was uploaded."
33444
+ },
33445
+ "avatar_url": {
33446
+ "type": "string",
33447
+ "format": "password",
33448
+ "x-basecamp-sensitive": {
33449
+ "category": "pii",
33450
+ "redact": true
33451
+ }
33452
+ },
33453
+ "url": {
33454
+ "type": "string"
33455
+ },
33456
+ "app_url": {
33457
+ "type": "string"
33458
+ },
33459
+ "bookmark_url": {
33460
+ "type": "string"
33461
+ },
33462
+ "room_url": {
33463
+ "type": "string",
33464
+ "description": "The Ping's Campfire (`/buckets/{bucketId}/chats/{campfireId}.json`)."
33465
+ },
33466
+ "edit_title_url": {
33467
+ "type": "string",
33468
+ "description": "Present only when the Ping can be renamed: one with more than one other\nperson in it."
33469
+ },
33470
+ "links": {
33471
+ "$ref": "#/components/schemas/PingLinks"
33472
+ },
33473
+ "people": {
33474
+ "type": "array",
33475
+ "items": {
33476
+ "$ref": "#/components/schemas/Person"
33477
+ },
33478
+ "description": "The people in the Ping other than the caller; the caller alone when\nnobody else is in it."
33479
+ }
33480
+ },
33481
+ "required": [
33482
+ "app_url",
33483
+ "avatar_url",
33484
+ "bookmark_url",
33485
+ "created_at",
33486
+ "id",
33487
+ "links",
33488
+ "named",
33489
+ "people",
33490
+ "room_url",
33491
+ "title",
33492
+ "updated_at",
33493
+ "url"
33494
+ ]
33495
+ },
33496
+ "PingLinks": {
33497
+ "type": "object",
33498
+ "description": "The Ping's Campfire endpoints, so a client need not derive them from\n`room_url`. The wire object also repeats `url`, `app_url`, `bookmark_url`\nand `edit_title_url` as `self`, `app`, `bookmark` and `edit_title`; those\nare read from the Ping's own fields.",
33499
+ "properties": {
33500
+ "lines": {
33501
+ "type": "string",
33502
+ "description": "The Campfire's lines (`/buckets/{bucketId}/chats/{campfireId}/lines.json`)."
33503
+ },
33504
+ "files": {
33505
+ "type": "string",
33506
+ "description": "The Campfire's uploads."
33507
+ }
33508
+ },
33509
+ "required": [
33510
+ "files",
33511
+ "lines"
33512
+ ]
33513
+ },
32783
33514
  "PollEventsResponseContent": {
32784
33515
  "type": "object",
32785
33516
  "description": "The poll envelope. Every 200 carries `events` and `position`; `next` only\nwhile the walk continues.",
@@ -36285,6 +37016,21 @@
36285
37016
  "UpdateAccountNameResponseContent": {
36286
37017
  "$ref": "#/components/schemas/Account"
36287
37018
  },
37019
+ "UpdateBoostRequestContent": {
37020
+ "type": "object",
37021
+ "properties": {
37022
+ "content": {
37023
+ "type": "string",
37024
+ "maxLength": 16
37025
+ }
37026
+ },
37027
+ "required": [
37028
+ "content"
37029
+ ]
37030
+ },
37031
+ "UpdateBoostResponseContent": {
37032
+ "$ref": "#/components/schemas/Boost"
37033
+ },
36288
37034
  "UpdateCalendarRequestContent": {
36289
37035
  "type": "object",
36290
37036
  "properties": {
@@ -37466,7 +38212,7 @@
37466
38212
  },
37467
38213
  "Wormhole": {
37468
38214
  "type": "object",
37469
- "description": "A wormhole links this card table to a column on another card table, enabling\ncards to move across projects. It carries the full recording representation\nplus the destination-linkage fields. The wormhole's own `url`/`app_url`/`parent`\npoint at the *source* board; `destination_url` is the only field identifying\nthe destination column.",
38215
+ "description": "A wormhole is a standing shortcut from this card table to a column on another\ncard table, usually in another project, and the only way to move a card across\nprojects. It carries the full recording representation\nplus the destination-linkage fields. The wormhole's own `url`/`app_url`/`parent`\npoint at the *source* board; `destination_url` is the only field identifying\nthe destination column.",
37470
38216
  "properties": {
37471
38217
  "id": {
37472
38218
  "type": "integer",