diff --git a/README.md b/README.md index 3186882..164bbb0 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,22 @@ The resource is called **person** (not user). Endpoints: - `/projects/{id}/people.json` — people in a project - `/my/profile.json` — current authenticated user (GET and PUT) +### Person types + +A full person object includes a `personable_type` saying what kind of person it is. The [people endpoints](sections/people.md) return full person objects, and most embedded people, such as a recording's `creator`, use them too. Some responses use a minimal person object with only `id`, `name`, and `avatar_url` instead, for example [out of office](sections/out_of_office.md) and the `assignees` in [my assignments](sections/my_assignments.md). Don't assume `personable_type` is present. + +The current types are: + +- `User` — a member of the account: a team member (`"client": false`) or a client member (`"client": true`). +- `Agent` — an AI agent added to the account. Agents create content and use the API as themselves. +- `Integration` — a chatbot or service integration that posts into Basecamp. +- `Outsider` — someone outside the account whose email reached Basecamp, such as the sender of an [email forward](sections/forwards.md). +- `Client` — a client contact from the legacy clientside ([client correspondences](sections/client_correspondences.md), [approvals](sections/client_approvals.md), and [replies](sections/client_replies.md)). These people have `"client": false`, so check `personable_type` to identify them. +- `DummyUser` — a sample person in the example projects Basecamp creates for new accounts. +- `Tombstone` — a person who has been removed from the account, or an agent or chatbot that has been deleted. Their past content keeps its author. + +**New person types may be added at any time, without a version change.** Treat `personable_type` as an open set: when you see a value you don't recognize, handle the person like any other (show their `name` and `avatar_url`) rather than failing to parse the response, and rely on the `admin`, `client`, and `can_*` flags, not the type, for what a person can do. + ### To-do specifics **Completion**: To-dos have a boolean `completed` field. By default, endpoints return active, pending (not completed) items. Use `?completed=true` for completed items. Use `?status=archived` or `?status=trashed` to see those. @@ -291,6 +307,7 @@ API endpoints - [Card table cards](sections/card_table_cards.md#card-table-cards) - [Card table columns](sections/card_table_columns.md#card-table-columns) - [Card table steps](sections/card_table_steps.md#card-table-steps) +- [Card table templates](sections/card_table_templates.md#card-table-templates) - [Card table wormholes](sections/card_table_wormholes.md#card-table-wormholes) - [Card tables](sections/card_tables.md#card-tables) - [Chatbots](sections/chatbots.md#chatbots) @@ -302,6 +319,7 @@ API endpoints - [Comments](sections/comments.md#comments) - [Documents](sections/documents.md#documents) - [Drafts](sections/drafts.md#drafts) +- [Event feed](sections/event_feed.md#event-feed) - [Events](sections/events.md#events) - [Everything](sections/everything.md#everything) - [External links](sections/external_links.md#external-links) @@ -336,6 +354,7 @@ API endpoints - [Subscriptions](sections/subscriptions.md#subscriptions) - [Subtasks](sections/subtasks.md#subtasks) - [Templates](sections/templates.md#templates) +- [Templatifications](sections/templatifications.md#templatifications) - [Timeline](sections/timeline.md#timeline) - [Timesheets](sections/timesheets.md#timesheets) - [To-do list groups](sections/todolist_groups.md#to-do-list-groups) diff --git a/sections/authentication.md b/sections/authentication.md index 60d80ab..4523dea 100644 --- a/sections/authentication.md +++ b/sections/authentication.md @@ -137,17 +137,15 @@ Get authorization from Basecamp Basecamp serves its own authorization document at the API root — no account prefix — for any token it accepts: a Basecamp-issued OAuth token or personal -access token, or a legacy Launchpad-issued token. It mirrors Launchpad's -document above, with a few deliberate differences: - -- `identity` carries only `id`. The name and email fields are omitted: they - were never suitable for identifying users within Basecamp (see the note on - the Launchpad document above) — use the [Get person][people] endpoints. -- Each account carries a `resource` indicator (`urn:bc:account:`, - RFC 8707) instead of Launchpad's `product` and `app_href`. Pass it as the - `resource` parameter when requesting a token scoped to that account. A - client that reads both documents must treat `product` and `app_href` as - optional and select accounts by `href` or `resource`. +access token, or a legacy Launchpad-issued token. It carries the same +`identity` and `accounts` fields as Launchpad's document above — the same +`product: "bc3"` selection works against either issuer, and the identity's +name and email remain **not** for identifying users within Basecamp (use the +[Get person][people] endpoints) — plus a few additions: + +- Each account also carries a `resource` indicator (`urn:bc:account:`, + RFC 8707). Pass it as the `resource` parameter when requesting a token + scoped to that account. - `scope` is present for every Basecamp-issued token — OAuth and personal access tokens alike. Legacy Launchpad-issued tokens predate scopes, so a missing `scope` is not an error. @@ -158,13 +156,18 @@ document above, with a few deliberate differences: ```json { "identity": { - "id": 9999999 + "id": 9999999, + "first_name": "Jason", + "last_name": "Fried", + "email_address": "jason@basecamp.com" }, "accounts": [ { + "product": "bc3", "id": 99999999, "name": "Honcho Design", "href": "https://3.basecampapi.com/99999999", + "app_href": "https://app.basecamp.com/99999999", "resource": "urn:bc:account:99999999" } ], diff --git a/sections/boosts.md b/sections/boosts.md index 47a8aa4..77e4b66 100644 --- a/sections/boosts.md +++ b/sections/boosts.md @@ -160,9 +160,9 @@ Create a boost * `POST /recordings/2/boosts.json` publishes a boost on the recording with an ID of `2`. * `POST /recordings/2/events/3/boosts.json` publishes a boost on the event with ID `3` on the recording with ID of `2`. Only boostable events (`completed`, `adopted`, `column_changed`) accept boosts; other events will return `403 Forbidden`. -**Required parameters**: `content` as the body of the boost. +**Required parameters**: `content` as the body of the boost, up to 191 characters. -This endpoint will return `201 Created` with the current JSON representation of the boost if the creation was a success. See the [Get a boost](#get-a-boost) endpoint for more info on the payload. +This endpoint will return `201 Created` with the current JSON representation of the boost if the creation was a success, or `422 Unprocessable Entity` if `content` is missing or too long. See the [Get a boost](#get-a-boost) endpoint for more info on the payload. ###### Example JSON Request diff --git a/sections/card_table_templates.md b/sections/card_table_templates.md new file mode 100644 index 0000000..96446bf --- /dev/null +++ b/sections/card_table_templates.md @@ -0,0 +1,1060 @@ +Card table templates +==================== + +Endpoints: + +- [Get the card table templates](#get-the-card-table-templates) +- [Create a card table template](#create-a-card-table-template) +- [Create a card table from a template](#create-a-card-table-from-a-template) + +Card table templates live in the same template library as [to-do list templates](template_library.md), in their own container. + +Get the card table templates +---------------------------- + +* `GET /template_library/card_tables.json` returns the account's template library, the container that holds its card table templates, and its active card table templates in title order. + +The bucket and card table IDs can be used with the existing [card table](card_tables.md), [column](card_table_columns.md), and [card](card_table_cards.md) endpoints to manage template contents. `kanban_boardset` is `null` for a library that has never held a card table template; creating the first one adds the container. + +###### Example JSON Response + +```json +{ + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "kanban_boardset": { + "id": 1069478891, + "title": "Card Table Templates", + "type": "Kanban::Boardset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/boardsets/1069478891.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/boardsets/1069478891" + }, + "card_tables": [ + { + "id": 1069480295, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.130Z", + "updated_at": "2026-09-12T08:21:52.238Z", + "title": "Client onboarding", + "inherits_status": true, + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5NT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--1c03f5d8c2bb14bcbc8e01fceda94830530f2ad6.json", + "parent": { + "id": 1069478891, + "title": "Card Table Templates", + "type": "Kanban::Boardset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/boardsets/1069478891.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/boardsets/1069478891" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + } + ] +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ + https://3.basecampapi.com/$ACCOUNT_ID/template_library/card_tables.json +``` + +Create a card table template +---------------------------- + +* `POST /template_library/card_tables.json` creates a card table template with the default columns. + +**Required parameters**: `name` of the template. + +###### Example JSON Request + + +```json +{ + "name": "Client onboarding" +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \ + -d '{"name":"Client onboarding"}' -X POST \ + https://3.basecampapi.com/$ACCOUNT_ID/template_library/card_tables.json +``` + +A successful request returns `201 Created` with the new [card table](card_tables.md). Its `parent` is the library's card table container, never the library's dock: a card table only counts as a template when it lives in that container, so a card table can't be added to the template library as a [dock tool](tools.md). + + +```json +{ + "id": 1069480295, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.130Z", + "updated_at": "2026-09-12T08:21:52.238Z", + "title": "Client onboarding", + "inherits_status": true, + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5NT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--1c03f5d8c2bb14bcbc8e01fceda94830530f2ad6.json", + "parent": { + "id": 1069478891, + "title": "Card Table Templates", + "type": "Kanban::Boardset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/boardsets/1069478891.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/boardsets/1069478891" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T03:17:10.372-05:00", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "subscribers": [], + "public_link_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480295/publication", + "lists": [ + { + "id": 1069480296, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.142Z", + "updated_at": "2026-09-12T08:21:52.142Z", + "title": "Triage", + "inherits_status": true, + "type": "Kanban::Triage", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/columns/1069480296.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/columns/1069480296", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5Nj9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--1d91948cb6130a83128e6e2b6d86f05abc6bd622.json", + "parent": { + "id": 1069480295, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/lists/1069480296/cards.json" + }, + { + "id": 1069480297, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.191Z", + "updated_at": "2026-09-12T08:21:52.191Z", + "title": "Not now", + "inherits_status": true, + "type": "Kanban::NotNowColumn", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/columns/1069480297.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/columns/1069480297", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5Nz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--f7f18821b60e3052ed7b30fe1b8e2fc627782fd4.json", + "parent": { + "id": 1069480295, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/lists/1069480297/cards.json" + }, + { + "id": 1069480298, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.200Z", + "updated_at": "2026-09-12T08:21:52.200Z", + "title": "Figuring it out", + "inherits_status": true, + "type": "Kanban::Column", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/columns/1069480298.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/columns/1069480298", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5OD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--a03a94461126863f9a47d9fe095fbb50c9976a88.json", + "position": 1, + "parent": { + "id": 1069480295, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [], + "color": "purple", + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/lists/1069480298/cards.json" + }, + { + "id": 1069480299, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.219Z", + "updated_at": "2026-09-12T08:21:52.219Z", + "title": "In progress", + "inherits_status": true, + "type": "Kanban::Column", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/columns/1069480299.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/columns/1069480299", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDI5OT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--e050da945fd200ac948118efc116a17b6135f57f.json", + "position": 2, + "parent": { + "id": 1069480295, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [], + "color": "orange", + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/lists/1069480299/cards.json" + }, + { + "id": 1069480300, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:52.238Z", + "updated_at": "2026-09-12T08:21:52.238Z", + "title": "Done", + "inherits_status": true, + "type": "Kanban::DoneColumn", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/columns/1069480300.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/columns/1069480300", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMwMD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--39c9513ef33d6a8a2fbc6c6c49a65fb77ebdc3a2.json", + "parent": { + "id": 1069480295, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/1069480295.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/card_tables/1069480295" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958495/card_tables/lists/1069480300/cards.json" + } + ], + "wormholes": [] +} +``` + + +Create a card table from a template +----------------------------------- + +* `POST /template_library/copies.json` starts copying a card table template onto a project's dock. + +**Required parameters**: + +* `template_recording_id` - the ID of a card table in the template library. +* `destination_project_id` - the ID of the [project](projects.md) to copy into. The card table lands on its dock. + +A `destination_parent_id` naming the dock recording is accepted instead, for callers that already have one. + +###### Example JSON Request + + +```json +{ + "template_recording_id": 1069480295, + "destination_project_id": 2085958504 +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \ + -d '{"template_recording_id":1069480295,"destination_project_id":2085958504}' -X POST \ + https://3.basecampapi.com/$ACCOUNT_ID/template_library/copies.json +``` + +The copy resource, its people confirmation step, and polling work exactly as for a [to-do list template](template_library.md#create-a-to-do-list-from-a-template). A completed copy includes the new card table as `destination_card_table`: + + +```json +{ + "id": 1, + "status": "completed", + "source_recording_id": 1069480295, + "destination_parent_id": 1069479828, + "url": "https://3.basecampapi.com/195539477/template_library/copies/1.json", + "destination_card_table": { + "id": 1069480307, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.232Z", + "updated_at": "2026-09-12T08:21:55.560Z", + "title": "Client onboarding", + "inherits_status": true, + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMwNz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--aeb04e386efa900d1be6e97dea023e3245f44646.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480307/subscription.json", + "position": 8, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "public_link_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480307/publication", + "lists": [ + { + "id": 1069480308, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.271Z", + "updated_at": "2026-09-12T08:21:55.280Z", + "title": "Triage", + "inherits_status": true, + "type": "Kanban::Triage", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/columns/1069480308.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/columns/1069480308", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMwOD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--1441007829dbc9fc8dd210472f1509df0d20c4f3.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480308/subscription.json", + "parent": { + "id": 1069480307, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" + }, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/lists/1069480308/cards.json" + }, + { + "id": 1069480309, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.299Z", + "updated_at": "2026-09-12T08:21:55.312Z", + "title": "Not now", + "inherits_status": true, + "type": "Kanban::NotNowColumn", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/columns/1069480309.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/columns/1069480309", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMwOT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--7fc939445bf671bcc7340b0cd836ab622d0d15a2.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480309/subscription.json", + "parent": { + "id": 1069480307, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" + }, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/lists/1069480309/cards.json" + }, + { + "id": 1069480310, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.336Z", + "updated_at": "2026-09-12T08:21:55.355Z", + "title": "Figuring it out", + "inherits_status": true, + "type": "Kanban::Column", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/columns/1069480310.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/columns/1069480310", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxMD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--b191977e44bd8b6541605970bcc32a217739f277.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480310/subscription.json", + "position": 1, + "parent": { + "id": 1069480307, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" + }, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "color": "purple", + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/lists/1069480310/cards.json" + }, + { + "id": 1069480311, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.392Z", + "updated_at": "2026-09-12T08:21:55.438Z", + "title": "In progress", + "inherits_status": true, + "type": "Kanban::Column", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/columns/1069480311.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/columns/1069480311", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxMT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--1316493d97fbabd8b36e6182478163aa85d528ce.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480311/subscription.json", + "position": 2, + "parent": { + "id": 1069480307, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" + }, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "color": "orange", + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/lists/1069480311/cards.json" + }, + { + "id": 1069480312, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:21:55.496Z", + "updated_at": "2026-09-12T08:21:55.521Z", + "title": "Done", + "inherits_status": true, + "type": "Kanban::DoneColumn", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/columns/1069480312.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/columns/1069480312", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxMj9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--3803ab4f4972d4bf8a0d324b084341e3e7b546a9.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480312/subscription.json", + "parent": { + "id": 1069480307, + "title": "Client onboarding", + "type": "Kanban::Board", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" + }, + "bucket": { + "id": 2085958504, + "name": "The Leto Laptop", + "type": "Project" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": null, + "subscribers": [ + { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + } + ], + "color": null, + "cards_count": 0, + "comment_count": 0, + "cards_url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/lists/1069480312/cards.json" + } + ], + "wormholes": [] + } +} +``` + diff --git a/sections/event_feed.md b/sections/event_feed.md new file mode 100644 index 0000000..0ab60f9 --- /dev/null +++ b/sections/event_feed.md @@ -0,0 +1,630 @@ +Event feed +========== + +The event feed is a resumable, account-wide notification feed for responsive +integrations — **not an audit log, and not guaranteed delivery**. It pairs a +live push stream (a wake-up signal over WebSocket) with catch-up polling +(best-effort recovery of anything push missed, with a measured bound). +Consumers should treat both lanes as notifications: deduplicate by event id +(inbox items by `addressing_id`, since one event can address you for several +reasons), and **refetch the referenced resource before acting on it**. + +The contract: + +- Live messages may be delayed, duplicated, or dropped; polling repairs missed + live delivery for events **with ids above your position** whose transactions + commit within a bounded safety delay (about 30 seconds). The bound is + defined relative to your position, not wall-clock: entering at the present + places everything already visible — and any still-uncommitted event that + drew an earlier id — behind your entry position for good (see + [Poll events](#poll-events)). The bound is measured server-side, but it is + not absolute: an unusually long-running transaction, or an event deleted + before it ever became poll-visible, can be missed entirely. Workflows that + need completeness must corroborate against the canonical resource APIs + rather than lean on the feed. +- Feed membership is fixed when an event is created; authorization is + evaluated when you read. Revoking someone's Basecamp access takes effect + immediately once the revocation commits (live streams are disconnected); + newly granted access shows up in polls immediately — including re-polls of + older positions — but requires reconnecting the live stream, since its + access set is built at subscribe. +- OAuth deauthorization is looser: it does not disconnect an already-open + live stream, and short-lived server-side authentication caching plus the + stream-ticket lifetime mean a deauthorized client's polls and freshly + minted tickets can keep working for a few minutes before going dark. That + grace window is best-effort and unpinned — it emerges from cache and + ticket lifetimes rather than a tested contract, so don't build on its + exact duration. +- Only cataloged event types are served (see [Event types](#event-types)). + Draft publication is delivered as the corresponding `.created` type. +- The catalog grows, and it grows under you: a poll or subscription with no + `types` filter is asking for the whole catalog, so new types start arriving + on the deploy that adds them, with no opt-in. Name the types you handle if + that matters to you. +- Workshop and not-yet-open spaces never feed: activity in project templates, + the template library, playground projects, and drafted or importing + projects is excluded at creation. Content constructed *by* machinery — + template application, recurring to-do generation — never feeds either; + the feed carries what people (and agents) do, not what construction + produces. Copies aren't served: a copied to-do or card arrives with its + history replayed silently and no event of its own. Moves between projects + are: a to-do or card moved in from another project is new activity there, + delivered as `todo.created` or `card.created`. Moves within a project are + served as `todo.moved` and `card.moved` — a to-do to another list or + group, a card to another column or on or off hold — as thin pointers, + with `card.moved` naming the columns in `details`. Copying a list or + card *out of a template* builds fresh to-dos and cards, so those are + served as ordinary creations. Circles (direct-message spaces) do feed. +- Positions are scoped to your account and filter set. Persist a position only + after you have accepted the events preceding it. +- The feed starts at a fixed epoch, an operational fence that can be raised + (for example, after a database rollback leaves an interval of history + unmarked for the feed). Requests below the epoch receive `410 Gone` with the + epoch and a `resume` URL that re-enters at the epoch with your filters + preserved, so the servable history above the fence is not skipped. History from before the feed shipped is never backfilled: + `since=0` replays served history only, back to the epoch, not the + account's whole past. + +Endpoints: + +- [Poll events](#poll-events) +- [Poll the inbox](#poll-the-inbox) +- [Create a stream ticket](#create-a-stream-ticket) + +Event types +----------- + +The feed serves a curated catalog of event types. Filter with the `types` +parameter (comma-separated). Currently: + +| Type | Fires when | +| --- | --- | +| `comment.created` | A comment is posted | +| `comment.content_changed` | A comment's body is edited | +| `message.created` | A message is posted or published from a draft | +| `message.content_changed` | A message's body is edited | +| `message.subject_changed` | A message's subject is edited | +| `todo.created` | A to-do is added, published, or moved in from another project | +| `todo.completed` | A to-do is completed | +| `todo.moved` | A to-do moves to another list, or to another group within its list | +| `todo.content_changed` | A to-do's name is edited | +| `todo.description_changed` | A to-do's notes are edited | +| `todo.scheduled` | A to-do gains a due or start date | +| `todo.rescheduled` | A dated to-do's due or start date moves | +| `todo.unscheduled` | A to-do's dates are cleared | +| `todo.assignment_changed` | A to-do's assignees change | +| `card.created` | A card table card is added or moved in from another project | +| `card.completed` | A card is completed | +| `card.moved` | A card moves to another column, or on or off hold; `details` carries `column_id` and `previous_column_id`, the containing columns (going on hold within a column reports the same column twice) | +| `card.content_changed` | A card's body is edited | +| `card.title_changed` | A card's title is edited | +| `card.due_on_changed` | A card's due date is set, moved, or cleared | +| `card.assignment_changed` | A card's assignees change | +| `chat.line.created` | A Campfire line is posted: text, rich text, code, upload, or integration lines (sound-command "plays" are excluded) | +| `boost.created` | Someone boosts a recording or an event on it (a to-do completion, say). `recording_id` is the recording either way; `details` carries `boost_id`, plus `boosted_event_id` and its `boosted_event_type` when the boost landed on an event (both null for a boost on the recording itself — the event's boosts are listed under the event, not the recording; `boosted_event_type` is also null when the boosted event's kind isn't cataloged). Removing a boost emits nothing. Boosts stay out of the legacy per-project webhooks; this feed and the inbox are their surface | +| `question.created` | An automatic check-in question is created | +| `question.answer.created` | Someone answers a check-in question | + +Edits are served so you can hear an instruction added to something already +posted, wherever the person put it — note that a to-do's `content` is its +name and its notes are its `description`. A save fires one event per field +changed, so editing a message's subject and its body together serves two +events, and repeated saves each fire their own — refetch the recording and +deduplicate on what you read, not on the number of events. A to-do's dates +are the exception: they move together under one scheduling event, so +dragging a spanned to-do serves one, not two. + +Dates move under different types on the two sides, because Basecamp records +them differently and the catalog exposes that rather than inventing a type +over it. A card's due date is a plain field: set, moved and cleared all arrive +as `card.due_on_changed`. A to-do's dates go through scheduling, which covers +its start date as well as its due date, and split three ways — +`todo.scheduled` when a to-do that had no dates gains one, `todo.rescheduled` +when a dated one moves, `todo.unscheduled` when they are cleared. Hearing every +date move on a to-do or a card means subscribing to all four. Dates elsewhere +are not served: not on a subtask, not on an event in the schedule, and not a +to-do's repetition schedule, which is a rule for making future to-dos rather +than a date on this one. + +Of the types above, one edit is missing: a comment's title. A comment has +one, but nobody writes it — it is derived from whatever the comment is on, +and the kind fires when that derivation catches up, usually on the comment's +next real edit, which is served under its own type. + +Serving edits added nothing outside the feed: an edit has never appeared in a +project's timeline or activity, and raises no notification of its own. The +exception is `todo.scheduled` and `todo.rescheduled`, which have notified a +to-do's assignees since long before the feed existed and still do; nothing +about that changed when they were cataloged. + +Editing to add a mention still notifies the person mentioned, as it always +has — that is the mention system, not this event. It reads the fields carrying +rich text, so a body or a to-do's notes reach the person named while a card's +title or a message's subject don't. The exception is a to-do with no notes, +where an `@callsign` in its name is read instead. + +Integration lines mean agents hear bots — and themselves. Deduplicate or +ignore your own creator id if you both post and listen. + +Pings (direct-message Campfires) ride `chat.line.created` like any other +chat line — there is no separate ping type. Filter on the Circle's bucket +id if you only want (or want to avoid) direct messages. + +The catalog grows as consumers need more types — ask! Catalog membership is +evaluated at read: if a type is ever removed, its history disappears from +polls too, rather than surviving as a frozen archive — and a `types` filter +that still names the removed type is rejected as an unknown type (the filter +`400`) until you drop it, which changes your filter set, so re-enter with +`since=` rather than resuming the held position. Additions cut the +other way — a newly cataloged type's history becomes servable back through +`since=0` replays, but a held position that already advanced past those +events never revisits them. + +Poll events +----------- + +* `GET /events.json` will return a list of events since your last position, + oldest first, in strict event-id order. + +Parameters: + +- With no `since` or `position`, the feed begins **at the present** — + equivalent to `since=now`. Historical replay is explicit: pass `since`. + Entering at the present sets your position to the newest visible event: + events already committed, and any in-flight event whose transaction drew an + earlier id and commits after your entry, fall behind that position and are + never served to it — the delivery bound covers ids above your position + only. Connect the live stream before entering (the recommended protocol) to + hear in-flight stragglers; or enter via `since=` from a known point. +- `since=` — start after the given event id. `since=0` replays all + served history back to the feed's epoch — under a raised epoch it enters at + the epoch rather than 410ing. `since=now` skips history and starts at the present. Use + `since` to enter the feed, to recover from an invalid position, or to + acknowledge a filter change. +- `position=` — resume from a position token issued by a previous + response. Positions are signed and opaque. +- `types`, `buckets`, `creators` — optional comma-separated filters. `buckets` + and `creators` accept at most 100 ids each; `types` accepts any subset of + the catalog with no separate count cap. Raw filter input is bounded before + parsing: array-form lists over 1,000 elements, or input over 16 KB in + either form, are rejected with the filter `400` (comma-form lists are + bounded by the byte cap and the id caps). Changing filters invalidates held positions (see below). +- `performers`, `exclude_performers` — filter by the **effective performer**: + the agent that carried out a delegated action (`performed_by_id`), else the + creator. At most 100 ids each; the literal `self` means the request's + effective actor — the agent on a delegated (agent-linked) token, otherwise + the authenticated person — and is resolved to that id server-side before + filtering and digesting, so continuation URLs and digests carry the id, + not the literal. + **`exclude_performers=self` is the loop guard**: an agent that acts on what + it hears should exclude its own performances rather than suppress all agent + activity. +- `actor_types` — opt-in filter by actor kind: `agent`, `person`, or both. + A delegated event's actor is the agent that performed it; a direct event's + actor is its creator, typed by what it currently is (humans, integrations, + and tombstones are `person`). This is a filter, not a default — agent + activity is real account activity, and suppressing it wholesale breaks + agent-to-agent workflows. Use `exclude_performers=self` to avoid echo. + +Every successful response is an envelope — the body is the contract: + +- `events` — up to 100 events, oldest first, in strict event-id order. Each + event is a thin pointer — `id`, `kind`, `action`, `event_type`, `bucket_id`, + `creator_id`, `performed_by_id`, `recording_id`, `created_at` — plus a + `details` object only for types that publish one (`boost.created`, `card.moved`); refetch + the referenced resource for anything else. Pages + along a walk may be empty while it crosses history your filters exclude + (the walk advances through bounded scan windows internally) — keep + following `next`. +- `position` — a durable position token for resuming later. Persist it only + after you've processed the page's events. Every `200` body carries + `position`; error responses carry their own shapes. +- `next` — a continuation URL, present only while the current walk has more + to serve; follow it to continue. When `next` is absent, this walk is done: + poll again later from the durable position. + +The `X-Feed-Position` and `Link: <...>; rel="next"` response headers echo +`position` and `next` as conveniences. + +Precisely, `next` appears when a page proved more servable history remains: a +full page of events below the walk's head, or a whole scan window that was +entirely outside the safety delay with event ids still remaining below the +head. A walk terminates at the head **frozen when the walk started**, not the +live head — events created mid-walk belong to the next poll. And a page cut +short by the safety delay deliberately withholds `next`: poll again from +`position` once those young events age out of the delay. + +Polls serve only events already outside a fixed safety delay, so they don't +skip events whose transactions are still committing — a measured bound, not a +guarantee (see the contract above). Events may therefore appear in polls up to +~30 seconds after they're pushed live; that's expected, so deduplicate by +event id. + +Error responses: + +- `400 Bad Request` — **branch on the body's `reason`, never on the wording of + `error`**, which is prose and may be reworded. Two values, with opposite + recoveries: + + `"invalid_position"` — a malformed **position** (including array-form + `position`/`since` parameters, and a `since` outside the signed 64-bit id + range). Recoverable: resume with `since=` or `since=now`. A + position minted for a different account is indistinguishable from a + malformed one and gets this same `400`. + + ```json + { + "error": "Unrecognized position. Resume with since= or since=now.", + "reason": "invalid_position" + } + ``` + + `"invalid_filter"` — a malformed **filter** (unknown `types`, non-integer + ids, more than 100 `buckets`/`creators` ids, oversized raw input, or a + dimension this resource doesn't apply to). Not recoverable by a cursor reset: + the error names the offending filter, so fix the filters. + + ```json + { + "error": "Unknown event types: nope.created. Fix the filters; a position reset won't help.", + "reason": "invalid_filter" + } + ``` + + **A `400` with no `reason` never reached the feed**, and neither recovery + above applies — fix the request itself. These are raised before the feed + gets the request and do not carry its error shape, so don't assume a `400` + body is JSON at all, or that an `error` in one means what the two above + mean: + + - a query string the server can't parse (invalid `%`-encoding, conflicting + `a[b]`/`a[b][c]` shapes, excessive nesting) answers the framework's own + `{"status": 400, "error": "Bad Request"}` when the request asks for JSON + — by `Accept` or by the `.json` extension — and an empty body otherwise. + Note that it reuses the `error` key for something unrelated; + - a missing `User-Agent` answers `text/plain` with no JSON at all (see + [Identifying your application](../README.md#identifying-your-application)). + + So: `reason` present, branch on it; `reason` absent, the request was + malformed before the feed saw it. +- `409 Conflict` — the position was minted for a different filter set. The + body names both sides: `position_digest` (the digest the position was + minted for) and `filters_digest` (the digest of the filters this request + presented). Re-enter with `since=` to acknowledge the filter change. +- `410 Gone` — the position predates the feed's epoch. The body carries + `epoch_after_id` and a `resume` URL re-entering at the epoch with your + canonical filters preserved. + +###### Example JSON Response + +```json +{ + "events": [ + { + "id": 1071915468, + "kind": "message_created", + "action": "created", + "created_at": "2026-07-14T06:10:00.159Z", + "event_type": "message.created", + "bucket_id": 2085958499, + "creator_id": 1049715945, + "performed_by_id": null, + "recording_id": 1069479766 + } + ], + "position": "aBcD..." +} +``` + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://3.basecampapi.com/$ACCOUNT_ID/events.json?since=now +``` + +Filter digests (srv2) +--------------------- + +Positions bind to their filter set through a versioned digest, published here +so SDKs can compute the server's canonical filter identity locally (for +checkpoint keys, and to interpret the `409` body): + +1. Canonicalize each present dimension: `types`, `actor_types`, and + `reasons` deduplicated and sorted bytewise (UTF-8); + `buckets`/`creators`/`performers`/`exclude_performers` coerced to base-10 + integers, deduplicated **after** coercion (`1` and `01` are the same id), + ascending — with the literal `self` resolved to the effective actor's id + (the agent on a delegated request, otherwise the authenticated person) + **before** digesting, so a position never means different filters for + different people. +2. Build a JSON object keyed by dimension name — **present dimensions + only**, keys sorted bytewise (`actor_types`, `buckets`, `creators`, + `exclude_performers`, `performers`, `reasons`, `types`). The empty filter + set is `{}`. +3. Serialize as minimal RFC 8259 JSON — no whitespace, no trailing newline. +4. Digest: SHA-256 of those bytes, first 16 lowercase hex characters + (8 bytes). + +Keying by name makes the scheme extension-stable: absent dimensions +contribute no bytes, so a newly introduced filter dimension never moves the +digest of any filter set that doesn't use it. + +The wire format is the bare 16-hex string — exactly what `position_digest` +and `filters_digest` carry in the `409` body. `srv2` names this scheme +version for client-side lineage keys (`srv2-`); the server never +emits the prefix. Any change to the canonicalization or algorithm ships as +`srv3` with new vectors — the vectors below are stable: + +| Input | Canonical JSON | Digest | +| --- | --- | --- | +| (no filters) | `{}` | `44136fa355b3678a` | +| `types=message.created` | `{"types":["message.created"]}` | `38b223c13c89dc89` | +| `types=todo.completed,message.created&buckets=2,1` | `{"buckets":[1,2],"types":["message.created","todo.completed"]}` | `90108e436c72bfd1` | +| `types=todo.completed,message.created&buckets=2,1&creators=9` | `{"buckets":[1,2],"creators":[9],"types":["message.created","todo.completed"]}` | `5d0b83594d8e5630` | +| `buckets=01` (also `buckets=1,01`) | `{"buckets":[1]}` | `e545e11ffb55966a` | +| `buckets=1,...,100` (the cap boundary) | `{"buckets":[1,2,...,100]}` | `e2acd9c77b72cd99` | +| `performers=9` (also `performers=self` when the effective actor is person 9) | `{"performers":[9]}` | `97c45ef5d7ed59c2` | +| `exclude_performers=9` | `{"exclude_performers":[9]}` | `e52acfcd22b34d9e` | +| `actor_types=agent` | `{"actor_types":["agent"]}` | `cfcc84d9873823db` | +| `reasons=mentioned` (inbox) | `{"reasons":["mentioned"]}` | `a68353156f17c45a` | +| `reasons=mentioned,assigned&performers=9&types=comment.created` | `{"performers":[9],"reasons":["assigned","mentioned"],"types":["comment.created"]}` | `99b78eea305639b8` | + +srv2's domain is the cataloged type strings, the actor-type strings `agent` +and `person`, the addressing-reason strings (`mentioned`, `assigned`, +`subscribed`, `watched`, `boosted`, `pinged` -- the inbox's reasons table +below), and integer ids, nothing else. The literal `self` is resolved to an +id before digesting and never appears in canonical JSON. Quoted or +non-ASCII types are outside the valid domain: unknown types, actor types, +and reasons are rejected with the filter `400` before any digest is +computed, so no vector for them exists. + +Poll the inbox +-------------- + +* `GET /inbox.json` will return the authenticated principal's **addressed + items** — the low-noise "someone addressed you" lane, as its own resource + rather than a filter over the account feed. **Agents only for now**: any + other principal receives `403 Forbidden`. People join when subscriptions + arrive. + +An item is a first-class delivery with its own identity: the same event can +address one person for several reasons, and each reason is its own item. +Deduplicate items by `addressing_id`, never by event id — the feed's rule +would discard every reason but one. Reasons: + +| Reason | You were… | +| --- | --- | +| `mentioned` | @mentioned in the content of a creation or publication, or newly @mentioned by an edit | +| `assigned` | assigned a to-do or card | +| `subscribed` | subscribed to the recording (or its container) the activity happened in | +| `watched` | watching a to-do list's additions or a card table column's cards, or a to-do's or card's completion | +| `pinged` | a participant in the Circle (Ping) the line was posted to | +| `boosted` | the person whose work was boosted | + +An edit addresses only what it added. Each edit event names the attribute it +changed — `message.content_changed` the body, `message.subject_changed` the +subject — and reads that attribute on both the new version and the one it +replaced, addressing the difference. So editing a body to add a mention +addresses once, on the body's event, even when the same save also renamed the +recording and served a second event alongside it. Leaving a mention where it +already was addresses nobody again, and an edit that touches no mention — +moving a date, rewriting a title that names nobody — addresses nobody at all. +Publishing a draft that gained the mention in the same save addresses once, +never twice — on the publication where it read the mention, on the edit's own +event where it didn't. + +Addressing is not the notification lane described further up, and the two +differ here. Addressing reads whatever text the named attribute holds, so an +`@callsign` typed into a message's subject or a card's title **does** address +you, and a to-do's name is read on its own terms rather than only when its +notes are blank. The one place that costs a second item: add the same mention +to two attributes in one save and each attribute's event addresses you — two +new mentions in two places, one item each. + +Items are never self-addressed: your own actions (as creator or performing +agent) are echo, not address. Being addressed doesn't confer access — an item +whose event you can no longer read is dropped at read time, exactly as the +feed drops it. + +Parameters: `since=0` replays the earliest items still retained (items are +kept for 30 days); `since=now` enters at the present; `position` resumes. +Filters: `reasons` (comma-separated, from the table above), plus `types` and +`buckets` as narrowing. Filter digests use the same srv2 scheme as the feed, +with `reasons` as its own dimension. Positions are bound to your account, +your person, and your filter set, and are never interchangeable with feed +positions. + +The envelope: `items` (oldest first, in strict item-id order), `position`, +and `next` while a walk has more to serve; `X-Feed-Position` and +`Link: rel="next"` echo them. Errors follow the feed's contract, except that +`410 Gone` here means the position fell behind the retention window — the +`resume` URL re-enters at `since=0`, the earliest retained item: a stale +position has seen none of the retained backlog, so that entry is exactly-once +continuation. + +###### Example JSON Response + +```json +{ + "items": [ + { + "addressing_id": 991, + "reason": "mentioned", + "addressed_at": "2026-07-14T06:10:00.159Z", + "event": { + "id": 1071915468, + "kind": "comment_created", + "action": "created", + "created_at": "2026-07-14T06:10:00.159Z", + "event_type": "comment.created", + "bucket_id": 2085958499, + "creator_id": 1049715945, + "performed_by_id": null, + "recording_id": 1069479766 + } + } + ], + "position": "aBcD..." +} +``` + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://3.basecampapi.com/$ACCOUNT_ID/inbox.json?since=0 +``` + +Live delivery: subscribe to `EventsChannel` with `"inbox": true` (plus any +`reasons`/`types`/`buckets`) on a ticket connection to receive each item as +it's written, in the same shape as above. An inbox subscription replaces the +account streams for that connection rather than adding to them. + +Agent-to-agent brakes: because two agents can address each other without +ever addressing themselves, inbox delivery carries brakes that self-exclusion +can't provide — a per-account delivery budget per minute, a circuit breaker +on runs of agent-performed deliveries (reset by the next human-performed +one), and an operator kill switch. A braked event is counted, not delivered; +if your agent goes quiet under a burst, that's why. + +Create a stream ticket +---------------------- + +* `POST /events/stream_ticket.json` mints a short-lived signed ticket (about + 2 minutes) for opening a live event stream over WebSocket, and returns the + exact `url` to connect to. + +Connect to the returned `url` verbatim — never assemble the WebSocket URL +(host, path, account prefix) yourself; the topology is the server's to change. +Both lanes serve agent principals: an agent's client-credentials token can +poll the feed and mint stream tickets for its own live stream, and access +revocation drops an agent's socket exactly as it drops a user's. A ticket +minted on a delegated request carries its agent, so `self` on the socket it +opens resolves to the agent exactly as it does for that request's polls. + +Mint a fresh ticket for every connection attempt: tickets expire in about two +minutes and are not refreshed by an open socket. A ticket is a replayable +bearer within that window — it is stateless and can open more than one +socket, so mint-per-connection is connector discipline, not server +enforcement. Every socket is bounded to a single subscription regardless. + +###### Example JSON Response + +```json +{ + "ticket": "aBcD...", + "expires_in": 120, + "url": "wss://chat.3.basecamp.com/195539477?ticket=aBcD..." +} +``` + +###### Copy as cURL + +```shell +curl -s -X POST -H "Authorization: Bearer $ACCESS_TOKEN" https://3.basecampapi.com/$ACCOUNT_ID/events/stream_ticket.json +``` + +Live stream +----------- + +Connect an Action Cable WebSocket client to the `url` from the mint, then +subscribe to `EventsChannel`: + +```json +{"command":"subscribe","identifier":"{\"channel\":\"EventsChannel\",\"types\":\"message.created,chat.line.created\"}"} +``` + +No `Origin` header is required for non-browser clients; browser-embedded +clients always send their page origin, which must be a Basecamp origin. + +Subscription parameters mirror the poll filters (`types`, `buckets`, +`creators`, `performers`, `exclude_performers` — `self` included — and +`actor_types`, with the same caps and bounds). Chat is high-volume and only streamed +when your `types` include chat types (or you subscribe unfiltered). Each +message is a wake-up payload: + +```json +{ + "id": 1071915468, + "kind": "message_created", + "event_type": "message.created", + "action": "created", + "created_at": "2026-07-14T06:10:00.159Z", + "bucket_id": 2085958499, + "creator_id": 1049715945, + "performed_by_id": null, + "actor_type": "person", + "recording_id": 1069479766, + "visible_to_clients": false +} +``` + +Live frames carry two transport-only fields the poll payloads don't: +`actor_type` and `visible_to_clients`, which exist so the cable fleet can +filter without touching the database. Null-valued fields are present on the +wire, as in the example; `details` appears exactly when the type publishes +one. + +### One subscription per connection + +A ticket connection carries **exactly one** `EventsChannel` subscription. +Retransmitting the identical subscribe while awaiting confirmation is fine +(that's the stock client's behavior); a subscribe with different parameters is +rejected. To change filters, reconnect with a fresh ticket. Subscriptions to +any other channel are rejected, and malformed or oversized commands close the +connection with `"reconnect":false` and the disconnect reason +`invalid_event_stream_command` — a terminal protocol error, not a cue to retry. + +### Liveness + +The server pings ticket connections every 3 seconds (`{"type":"ping"}`). +Treat the connection as stale after two missed beats; a stock Action Cable +client's connection monitor detects that on its own jittered interval, +roughly 6–12 seconds. + +The stream is usable only after the server confirms your subscription +(`confirm_subscription`) — an open socket is not a subscribed socket, since +confirmation waits on server-side stream registration. Start a confirmation +deadline (10 seconds is a good default) when you send the initial subscribe; +cancel it on confirmation, rejection, or disconnect. If the deadline lapses, +dispose the subscription and consumer, then reconnect with a fresh ticket. + +An explicit subscription **rejection** (bad filters, restricted channel) does +not close the socket. Treat it as terminal configuration error: dispose the +consumer (closing the socket) and surface the error — do not reconnect into +the same rejection. + +### Reconnection + +**A stock Action Cable client cannot reconnect a ticket stream on its own.** +Its monitor reopens the connection with the same URL it first connected with, +so after any outage longer than the ~2-minute ticket lifetime it re-presents +an expired ticket and is rejected forever. Staleness *detection* works out of +the box; recovery does not. + +Own reconnection in your wrapper: + +1. Disable or intercept the stock automatic reconnect. +2. On disconnect (or a lapsed confirmation deadline), mint a fresh ticket + asynchronously. +3. Create or reconnect the consumer with the new `url`, resubscribe, and wait + for confirmation again. +4. Run at most one reconnect attempt at a time, with jittered backoff between + attempts. + +Client protocol +--------------- + +The recommended consumption loop: + +1. Mint a ticket, connect to its `url`, subscribe, await confirmation, and + **buffer** incoming live events. +2. Poll from your last durable position until `next` disappears from the + envelope, persisting events and then the position as you go. +3. Drain the buffer, deduplicating by event id, then act on live events as + they arrive. +4. Poll periodically (e.g. every 60 seconds) from your durable position to + repair anything push missed. + +**Live event ids never advance your durable position — only poll positions +do.** Positions persist across reconnects and restarts; tickets don't, so +mint a fresh ticket per connection. diff --git a/sections/people.md b/sections/people.md index a262988..c8e1796 100644 --- a/sections/people.md +++ b/sections/people.md @@ -23,6 +23,8 @@ Get all people Wherever a person is represented, the `email_address` is only returned in full to account administrators and owners, and to the person themselves. For everyone else it is redacted (e.g. `"j••••@•••••••••.•••"`). +The `personable_type` field says what kind of person this is — `User`, `Agent`, `Tombstone`, and so on. New types may appear at any time, so handle a value you don't recognize gracefully. See [Person types](../README.md#person-types). + ###### Example JSON Response ```json @@ -499,7 +501,10 @@ Update my personal info **Optional parameters**: * `name` - the user's display name. -* `email_address` - the user's email address. +* `email_address` - the user's email address. This is the 37signals login identity shared across + every account, so changing it requires a trusted client with session-equivalent authority: a + full-scope, identity-wide token (a personal access token, or a first-party app's identity-wide + token). Any other token gets `403 Forbidden` with an `error` message and changes nothing. * `title` - the user's job title. * `bio` - a short bio. * `location` - the user's location. diff --git a/sections/projects.md b/sections/projects.md index 6c321c6..dd67ac9 100644 --- a/sections/projects.md +++ b/sections/projects.md @@ -29,95 +29,101 @@ Two flags describe the project's place on the current user's home page: `bookmar ```json [ { - "id": 2085958505, + "id": 2085958504, "status": "active", - "created_at": "2026-04-13T20:24:00.000Z", - "updated_at": "2026-07-21T01:05:29.829Z", + "created_at": "2026-07-29T18:30:00.000Z", + "updated_at": "2026-09-12T08:21:50.481Z", "name": "The Leto Laptop", "description": "Laptop product launch.", "purpose": "topic", "clients_enabled": false, "timesheet_enabled": true, "color": null, - "last_needle_color": "yellow", - "last_needle_position": 42, - "previous_needle_position": null, - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwNT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--427e605de283e100f75da40006030176fd863024.json", - "star_url": "https://3.basecampapi.com/195539477/buckets/2085958505/stars.json", - "url": "https://3.basecampapi.com/195539477/projects/2085958505.json", - "app_url": "https://3.basecamp.com/195539477/projects/2085958505", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwND9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--63c6529d88eaffe640cc1ca84888819d8d327108.json", + "star_url": "https://3.basecampapi.com/195539477/buckets/2085958504/stars.json", + "url": "https://3.basecampapi.com/195539477/projects/2085958504.json", + "app_url": "https://3.basecamp.com/195539477/projects/2085958504", "dock": [ { - "id": 1069479828, + "id": 1069479829, "title": "Message Board", "name": "message_board", "enabled": true, "position": 1, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/message_boards/1069479828.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/message_boards/1069479828" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/message_boards/1069479829.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/message_boards/1069479829" }, { - "id": 1069479829, + "id": 1069479830, "title": "To-dos", "name": "todoset", "enabled": true, "position": 2, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/todosets/1069479829.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/todosets/1069479829" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todosets/1069479830.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todosets/1069479830" }, { - "id": 1069479830, + "id": 1069479831, "title": "Docs & Files", "name": "vault", "enabled": true, "position": 3, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/vaults/1069479830.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/vaults/1069479830" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/vaults/1069479831.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/vaults/1069479831" }, { - "id": 1069479831, + "id": 1069479832, "title": "Calendar", "name": "schedule", "enabled": true, "position": 4, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/schedules/1069479831.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/schedules/1069479831" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/schedules/1069479832.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/schedules/1069479832" }, { - "id": 1069479832, + "id": 1069479833, "title": "Chat", "name": "chat", "enabled": true, "position": 5, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/chats/1069479832.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/chats/1069479832" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/chats/1069479833.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/chats/1069479833" }, { - "id": 1069479833, + "id": 1069479834, "title": "Card Table", "name": "kanban_board", "enabled": true, "position": 7, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/card_tables/1069479833.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/card_tables/1069479833" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069479834.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069479834" }, { - "id": 1069479839, + "id": 1069479840, "title": "Automatic Check-ins", "name": "questionnaire", "enabled": true, "position": 6, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/questionnaires/1069479839.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/questionnaires/1069479839" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/questionnaires/1069479840.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/questionnaires/1069479840" }, { - "id": 1069479840, + "id": 1069479841, "title": "Email Forwards", "name": "inbox", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/inboxes/1069479840.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/inboxes/1069479840" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/inboxes/1069479841.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/inboxes/1069479841" + }, + { + "id": 1069480307, + "title": "Client onboarding", + "name": "kanban_board", + "enabled": true, + "position": 8, + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" } ], "people": { @@ -172,100 +178,100 @@ Two flags describe the project's place on the current user's home page: `bookmar "starred": false }, { - "id": 2085958506, + "id": 2085958505, "status": "active", - "created_at": "2026-04-13T15:45:00.000Z", - "updated_at": "2026-07-20T04:05:52.507Z", + "created_at": "2026-07-29T17:16:00.000Z", + "updated_at": "2026-09-12T08:19:06.771Z", "name": "The Leto Locator", "description": "New software and hardware built for locating and securing Leto products.", "purpose": "topic", "clients_enabled": false, "timesheet_enabled": false, "color": null, - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwNj9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--c1f4e47bbc088a5055d422c816957a763ed42aec.json", - "star_url": "https://3.basecampapi.com/195539477/buckets/2085958506/stars.json", - "url": "https://3.basecampapi.com/195539477/projects/2085958506.json", - "app_url": "https://3.basecamp.com/195539477/projects/2085958506", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwNT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--427e605de283e100f75da40006030176fd863024.json", + "star_url": "https://3.basecampapi.com/195539477/buckets/2085958505/stars.json", + "url": "https://3.basecampapi.com/195539477/projects/2085958505.json", + "app_url": "https://3.basecamp.com/195539477/projects/2085958505", "client_company": { "id": 1033447818, "name": "Leto Brand" }, "clientside": { - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/client/board.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/client/board" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/client/board.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/client/board" }, "dock": [ { - "id": 1069480045, + "id": 1069480049, "title": "Message Board", "name": "message_board", "enabled": true, "position": 1, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/message_boards/1069480045.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/message_boards/1069480045" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/message_boards/1069480049.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/message_boards/1069480049" }, { - "id": 1069480046, + "id": 1069480050, "title": "To-dos", "name": "todoset", "enabled": true, "position": 2, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/todosets/1069480046.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/todosets/1069480046" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/todosets/1069480050.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/todosets/1069480050" }, { - "id": 1069480047, + "id": 1069480051, "title": "Docs & Files", "name": "vault", "enabled": true, "position": 3, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/vaults/1069480047.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/vaults/1069480047" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/vaults/1069480051.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/vaults/1069480051" }, { - "id": 1069480048, + "id": 1069480052, "title": "Calendar", "name": "schedule", "enabled": true, "position": 4, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/schedules/1069480048.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/schedules/1069480048" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/schedules/1069480052.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/schedules/1069480052" }, { - "id": 1069480049, + "id": 1069480053, "title": "Chat", "name": "chat", "enabled": true, "position": 5, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/chats/1069480049.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/chats/1069480049" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/chats/1069480053.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/chats/1069480053" }, { - "id": 1069480050, + "id": 1069480054, "title": "Card Table", "name": "kanban_board", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/card_tables/1069480050.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/card_tables/1069480050" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/card_tables/1069480054.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/card_tables/1069480054" }, { - "id": 1069480056, + "id": 1069480060, "title": "Automatic Check-ins", "name": "questionnaire", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/questionnaires/1069480056.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/questionnaires/1069480056" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/questionnaires/1069480060.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/questionnaires/1069480060" }, { - "id": 1069480057, + "id": 1069480061, "title": "Email Forwards", "name": "inbox", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958506/inboxes/1069480057.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958506/inboxes/1069480057" + "url": "https://3.basecampapi.com/195539477/buckets/2085958505/inboxes/1069480061.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/inboxes/1069480061" } ], "people": { @@ -342,95 +348,101 @@ The `dock` key contains an array of the current tools for this project. The `ena ```json { - "id": 2085958505, + "id": 2085958504, "status": "active", - "created_at": "2026-04-13T20:24:00.000Z", - "updated_at": "2026-07-21T01:05:29.829Z", + "created_at": "2026-07-29T18:30:00.000Z", + "updated_at": "2026-09-12T08:21:50.481Z", "name": "The Leto Laptop", "description": "Laptop product launch.", "purpose": "topic", "clients_enabled": false, "timesheet_enabled": true, "color": null, - "last_needle_color": "yellow", - "last_needle_position": 42, - "previous_needle_position": null, - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwNT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--427e605de283e100f75da40006030176fd863024.json", - "star_url": "https://3.basecampapi.com/195539477/buckets/2085958505/stars.json", - "url": "https://3.basecampapi.com/195539477/projects/2085958505.json", - "app_url": "https://3.basecamp.com/195539477/projects/2085958505", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwND9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--63c6529d88eaffe640cc1ca84888819d8d327108.json", + "star_url": "https://3.basecampapi.com/195539477/buckets/2085958504/stars.json", + "url": "https://3.basecampapi.com/195539477/projects/2085958504.json", + "app_url": "https://3.basecamp.com/195539477/projects/2085958504", "dock": [ { - "id": 1069479828, + "id": 1069479829, "title": "Message Board", "name": "message_board", "enabled": true, "position": 1, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/message_boards/1069479828.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/message_boards/1069479828" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/message_boards/1069479829.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/message_boards/1069479829" }, { - "id": 1069479829, + "id": 1069479830, "title": "To-dos", "name": "todoset", "enabled": true, "position": 2, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/todosets/1069479829.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/todosets/1069479829" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todosets/1069479830.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todosets/1069479830" }, { - "id": 1069479830, + "id": 1069479831, "title": "Docs & Files", "name": "vault", "enabled": true, "position": 3, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/vaults/1069479830.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/vaults/1069479830" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/vaults/1069479831.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/vaults/1069479831" }, { - "id": 1069479831, + "id": 1069479832, "title": "Calendar", "name": "schedule", "enabled": true, "position": 4, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/schedules/1069479831.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/schedules/1069479831" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/schedules/1069479832.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/schedules/1069479832" }, { - "id": 1069479832, + "id": 1069479833, "title": "Chat", "name": "chat", "enabled": true, "position": 5, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/chats/1069479832.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/chats/1069479832" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/chats/1069479833.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/chats/1069479833" }, { - "id": 1069479833, + "id": 1069479834, "title": "Card Table", "name": "kanban_board", "enabled": true, "position": 7, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/card_tables/1069479833.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/card_tables/1069479833" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069479834.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069479834" }, { - "id": 1069479839, + "id": 1069479840, "title": "Automatic Check-ins", "name": "questionnaire", "enabled": true, "position": 6, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/questionnaires/1069479839.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/questionnaires/1069479839" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/questionnaires/1069479840.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/questionnaires/1069479840" }, { - "id": 1069479840, + "id": 1069479841, "title": "Email Forwards", "name": "inbox", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/inboxes/1069479840.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/inboxes/1069479840" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/inboxes/1069479841.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/inboxes/1069479841" + }, + { + "id": 1069480307, + "title": "Client onboarding", + "name": "kanban_board", + "enabled": true, + "position": 8, + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" } ], "people": { @@ -505,95 +517,101 @@ A visit is recorded when the user opens a project in Basecamp, when they create ```json [ { - "id": 2085958505, + "id": 2085958504, "status": "active", - "created_at": "2026-04-13T20:24:00.000Z", - "updated_at": "2026-07-21T01:05:29.829Z", + "created_at": "2026-07-29T18:30:00.000Z", + "updated_at": "2026-09-12T08:21:50.481Z", "name": "The Leto Laptop", "description": "Laptop product launch.", "purpose": "topic", "clients_enabled": false, "timesheet_enabled": true, "color": null, - "last_needle_color": "yellow", - "last_needle_position": 42, - "previous_needle_position": null, - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwNT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--427e605de283e100f75da40006030176fd863024.json", - "star_url": "https://3.basecampapi.com/195539477/buckets/2085958505/stars.json", - "url": "https://3.basecampapi.com/195539477/projects/2085958505.json", - "app_url": "https://3.basecamp.com/195539477/projects/2085958505", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9CdWNrZXQvMjA4NTk1ODUwND9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--63c6529d88eaffe640cc1ca84888819d8d327108.json", + "star_url": "https://3.basecampapi.com/195539477/buckets/2085958504/stars.json", + "url": "https://3.basecampapi.com/195539477/projects/2085958504.json", + "app_url": "https://3.basecamp.com/195539477/projects/2085958504", "dock": [ { - "id": 1069479828, + "id": 1069479829, "title": "Message Board", "name": "message_board", "enabled": true, "position": 1, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/message_boards/1069479828.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/message_boards/1069479828" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/message_boards/1069479829.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/message_boards/1069479829" }, { - "id": 1069479829, + "id": 1069479830, "title": "To-dos", "name": "todoset", "enabled": true, "position": 2, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/todosets/1069479829.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/todosets/1069479829" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todosets/1069479830.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todosets/1069479830" }, { - "id": 1069479830, + "id": 1069479831, "title": "Docs & Files", "name": "vault", "enabled": true, "position": 3, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/vaults/1069479830.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/vaults/1069479830" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/vaults/1069479831.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/vaults/1069479831" }, { - "id": 1069479831, + "id": 1069479832, "title": "Calendar", "name": "schedule", "enabled": true, "position": 4, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/schedules/1069479831.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/schedules/1069479831" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/schedules/1069479832.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/schedules/1069479832" }, { - "id": 1069479832, + "id": 1069479833, "title": "Chat", "name": "chat", "enabled": true, "position": 5, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/chats/1069479832.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/chats/1069479832" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/chats/1069479833.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/chats/1069479833" }, { - "id": 1069479833, + "id": 1069479834, "title": "Card Table", "name": "kanban_board", "enabled": true, "position": 7, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/card_tables/1069479833.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/card_tables/1069479833" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069479834.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069479834" }, { - "id": 1069479839, + "id": 1069479840, "title": "Automatic Check-ins", "name": "questionnaire", "enabled": true, "position": 6, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/questionnaires/1069479839.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/questionnaires/1069479839" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/questionnaires/1069479840.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/questionnaires/1069479840" }, { - "id": 1069479840, + "id": 1069479841, "title": "Email Forwards", "name": "inbox", "enabled": false, "position": null, - "url": "https://3.basecampapi.com/195539477/buckets/2085958505/inboxes/1069479840.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958505/inboxes/1069479840" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/inboxes/1069479841.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/inboxes/1069479841" + }, + { + "id": 1069480307, + "title": "Client onboarding", + "name": "kanban_board", + "enabled": true, + "position": 8, + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/card_tables/1069480307.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/card_tables/1069480307" } ], "people": { diff --git a/sections/template_library.md b/sections/template_library.md index e788537..8cec7e8 100644 --- a/sections/template_library.md +++ b/sections/template_library.md @@ -4,18 +4,21 @@ To-do list templates Endpoints: - [Get the template library](#get-the-template-library) +- [Create a to-do list template](#create-a-to-do-list-template) - [Create a to-do list from a template](#create-a-to-do-list-from-a-template) - [Get a template copy](#get-a-template-copy) Get the template library ------------------------ -* `GET /template_library.json` returns the account's to-do list template library, its to-do set, and its active to-do list templates in title order. +* `GET /template_library/todolists.json` returns the account's to-do list template library, its to-do set, and its active to-do list templates in title order. -The bucket and to-do set IDs can be used with the existing [to-do list](todolists.md) and [to-do](todos.md) endpoints to create and manage template contents. +The bucket and to-do set IDs can be used with the existing [to-do list](todolists.md) and [to-do](todos.md) endpoints to create and manage template contents. [Card table templates](card_table_templates.md) have their own endpoints. + +`GET /template_library.json` is the former address of this endpoint, from when the library held only to-do list templates. It still answers, redirecting here and preserving the requested format. Prefer the address above: the redirect costs a round trip and says nothing about which kind of template it returns. ###### Example JSON Response - + ```json { "bucket": { @@ -24,37 +27,37 @@ The bucket and to-do set IDs can be used with the existing [to-do list](todolist "type": "TemplateLibrary" }, "todoset": { - "id": 1069478899, + "id": 1069478890, "title": "To-do List Templates", "type": "Todoset", - "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478899.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478899" + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" }, "todolists": [ { - "id": 1069480199, + "id": 1069480315, "status": "active", "visible_to_clients": false, - "created_at": "2026-08-27T16:03:42.233Z", - "updated_at": "2026-08-27T16:03:42.974Z", + "created_at": "2026-09-12T08:21:58.922Z", + "updated_at": "2026-09-12T08:21:59.692Z", "title": "Assigned launch checklist", "inherits_status": true, "type": "Todolist", - "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480199.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480199", - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDE5OT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--c8f44544c2376fdb5fa8216cb38cbbd5afd3b95e.json", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480315.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480315", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxNT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--17548aea6556917fc5cb0b2da02e6edf99683c48.json", "comments_count": 0, - "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480199/comments.json", + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480315/comments.json", "boosts_count": 0, - "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480199/boosts.json", - "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480199/bubble_up.json", - "position": 1, + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480315/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480315/bubble_up.json", + "position": 2, "parent": { - "id": 1069478899, + "id": 1069478890, "title": "To-do List Templates", "type": "Todoset", - "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478899.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478899" + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" }, "bucket": { "id": 2085958495, @@ -69,8 +72,8 @@ The bucket and to-do set IDs can be used with the existing [to-do list](todolist "title": "Chief Strategist", "tagline": "Don't let your dreams be dreams", "location": "Chicago, IL", - "created_at": "2026-08-27T16:01:09.786Z", - "updated_at": "2026-08-27T16:01:10.505Z", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", "email_address": "victor@honchodesign.com", "bio": "Don't let your dreams be dreams", "admin": true, @@ -95,35 +98,104 @@ The bucket and to-do set IDs can be used with the existing [to-do list](todolist "completed_ratio": "0/1", "name": "Assigned launch checklist", "color": null, - "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480199/groups.json", - "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480199/todos.json", - "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480199/todos", - "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480199/comments" + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480315/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480315/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480315/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480315/comments" + }, + { + "id": 1069480317, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:22:00.082Z", + "updated_at": "2026-09-12T08:22:00.082Z", + "title": "New hire setup", + "inherits_status": true, + "type": "Todolist", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480317", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxNz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--b3397ba9708eefe03ea717fa47674178dc50e830.json", + "comments_count": 0, + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/comments.json", + "boosts_count": 0, + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/bubble_up.json", + "position": 1, + "parent": { + "id": 1069478890, + "title": "To-do List Templates", + "type": "Todoset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": "", + "description_attachments": [], + "completed": false, + "completed_ratio": "0/0", + "name": "New hire setup", + "color": null, + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480317/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480317/comments" }, { - "id": 1069480198, + "id": 1069480314, "status": "active", "visible_to_clients": false, - "created_at": "2026-08-27T16:03:41.820Z", - "updated_at": "2026-08-27T16:03:41.820Z", + "created_at": "2026-09-12T08:21:58.556Z", + "updated_at": "2026-09-12T08:21:58.556Z", "title": "Project kickoff", "inherits_status": true, "type": "Todolist", - "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480198.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480198", - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDE5OD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--c8c70a454e67b2c5c3c2cb5edd7eef362e936011.json", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480314.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480314", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxND9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--2e1c2130d64df5d6cf4baa60fabe92a89f6958a4.json", "comments_count": 0, - "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480198/comments.json", + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480314/comments.json", "boosts_count": 0, - "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480198/boosts.json", - "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480198/bubble_up.json", - "position": 2, + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480314/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480314/bubble_up.json", + "position": 3, "parent": { - "id": 1069478899, + "id": 1069478890, "title": "To-do List Templates", "type": "Todoset", - "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478899.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478899" + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" }, "bucket": { "id": 2085958495, @@ -138,8 +210,8 @@ The bucket and to-do set IDs can be used with the existing [to-do list](todolist "title": "Chief Strategist", "tagline": "Don't let your dreams be dreams", "location": "Chicago, IL", - "created_at": "2026-08-27T16:01:09.786Z", - "updated_at": "2026-08-27T16:01:10.505Z", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", "email_address": "victor@honchodesign.com", "bio": "Don't let your dreams be dreams", "admin": true, @@ -164,21 +236,124 @@ The bucket and to-do set IDs can be used with the existing [to-do list](todolist "completed_ratio": "0/0", "name": "Project kickoff", "color": null, - "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480198/groups.json", - "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480198/todos.json", - "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480198/todos", - "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480198/comments" + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480314/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480314/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480314/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480314/comments" } ] } ``` - + ###### Copy as cURL ```shell curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ - https://3.basecampapi.com/$ACCOUNT_ID/template_library.json + https://3.basecampapi.com/$ACCOUNT_ID/template_library/todolists.json +``` + +Create a to-do list template +---------------------------- + +* `POST /template_library/todolists.json` creates an empty to-do list template. + +* `name` - what to call the template. Required. +* `description` - rich text describing the template. Optional. + +Answers `201 Created` with the template. Fill it in with the [to-do](todos.md) endpoints, using the returned ID. To start from a to-do list that already exists in a project, [templatify it](templatifications.md) instead. + +###### Example JSON Request + +```json +{ + "name": "New hire setup" +} +``` + + +###### Example JSON Response + +```json +{ + "id": 1069480317, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:22:00.082Z", + "updated_at": "2026-09-12T08:22:00.082Z", + "title": "New hire setup", + "inherits_status": true, + "type": "Todolist", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480317", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxNz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--b3397ba9708eefe03ea717fa47674178dc50e830.json", + "comments_count": 0, + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/comments.json", + "boosts_count": 0, + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480317/bubble_up.json", + "position": 1, + "parent": { + "id": 1069478890, + "title": "To-do List Templates", + "type": "Todoset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T03:17:10.372-05:00", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": "", + "description_attachments": [], + "completed": false, + "completed_ratio": "0/0", + "name": "New hire setup", + "color": null, + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480317/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480317/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480317/comments" +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"Client onboarding"}' -X POST \ + https://3.basecampapi.com/$ACCOUNT_ID/template_library/todolists.json ``` Create a to-do list from a template @@ -189,15 +364,17 @@ Create a to-do list from a template **Required parameters**: * `template_recording_id` - the ID of a to-do list in the template library. -* `destination_parent_id` - the ID of the destination project's to-do set. +* `destination_project_id` - the ID of the [project](projects.md) to copy into. The to-do list lands in its to-do set. + +A `destination_parent_id` naming the to-do set is accepted instead, for callers that already have one. ###### Example JSON Request ```json { - "template_recording_id": 1069480198, - "destination_parent_id": 1069479838 + "template_recording_id": 1069480314, + "destination_project_id": 2085958504 } ``` @@ -206,7 +383,7 @@ Create a to-do list from a template ```shell curl -s -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \ - -d '{"template_recording_id":1069480198,"destination_parent_id":1069479838}' -X POST \ + -d '{"template_recording_id":1069480314,"destination_project_id":2085958504}' -X POST \ https://3.basecampapi.com/$ACCOUNT_ID/template_library/copies.json ``` @@ -215,11 +392,11 @@ A successful request returns `201 Created` with a copy resource. Follow its `url ```json { - "id": 3, + "id": 2, "status": "pending", - "source_recording_id": 1069480198, - "destination_parent_id": 1069479838, - "url": "https://3.basecampapi.com/195539477/template_library/copies/3.json" + "source_recording_id": 1069480314, + "destination_parent_id": 1069479830, + "url": "https://3.basecampapi.com/195539477/template_library/copies/2.json" } ``` @@ -256,36 +433,36 @@ The status is `pending`, `processing`, `completed`, or `failed`. Poll the URL no ```json { - "id": 3, + "id": 2, "status": "completed", - "source_recording_id": 1069480198, - "destination_parent_id": 1069479838, - "url": "https://3.basecampapi.com/195539477/template_library/copies/3.json", + "source_recording_id": 1069480314, + "destination_parent_id": 1069479830, + "url": "https://3.basecampapi.com/195539477/template_library/copies/2.json", "destination_todolist": { - "id": 1069480208, + "id": 1069480318, "status": "active", "visible_to_clients": false, - "created_at": "2026-08-27T16:04:17.805Z", - "updated_at": "2026-08-27T16:04:17.837Z", + "created_at": "2026-09-12T08:22:02.924Z", + "updated_at": "2026-09-12T08:22:02.994Z", "title": "Project kickoff", "inherits_status": true, "type": "Todolist", - "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480208.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todolists/1069480208", - "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDIwOD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--0494faefccb620dc9b3828034fb8c1600bae73f6.json", - "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480208/subscription.json", + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480318.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todolists/1069480318", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxOD9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--bd1dfbe4497ccccd37f42e32f2ac77a9059a6229.json", + "subscription_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480318/subscription.json", "comments_count": 0, - "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480208/comments.json", + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480318/comments.json", "boosts_count": 0, - "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480208/boosts.json", - "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480208/bubble_up.json", + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480318/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069480318/bubble_up.json", "position": 3, "parent": { - "id": 1069479838, + "id": 1069479830, "title": "To-dos", "type": "Todoset", - "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todosets/1069479838.json", - "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todosets/1069479838" + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/todosets/1069479830.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958504/todosets/1069479830" }, "bucket": { "id": 2085958504, @@ -300,8 +477,8 @@ The status is `pending`, `processing`, `completed`, or `failed`. Poll the URL no "title": "Chief Strategist", "tagline": "Don't let your dreams be dreams", "location": "Chicago, IL", - "created_at": "2026-08-27T16:01:09.786Z", - "updated_at": "2026-08-27T16:01:10.505Z", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", "email_address": "victor@honchodesign.com", "bio": "Don't let your dreams be dreams", "admin": true, @@ -326,10 +503,10 @@ The status is `pending`, `processing`, `completed`, or `failed`. Poll the URL no "completed_ratio": "0/0", "name": "Project kickoff", "color": null, - "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480208/groups.json", - "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480208/todos.json", - "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958504/todolists/1069480208/todos", - "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958504/recordings/1069480208/comments" + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480318/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958504/todolists/1069480318/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958504/todolists/1069480318/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958504/recordings/1069480318/comments" } } ``` diff --git a/sections/templatifications.md b/sections/templatifications.md new file mode 100644 index 0000000..785aced --- /dev/null +++ b/sections/templatifications.md @@ -0,0 +1,155 @@ +Templatifications +================= + +Endpoints: + +- [Templatify a to-do list or card table](#templatify-a-to-do-list-or-card-table) +- [Get a templatification](#get-a-templatification) + +Templatifying turns a to-do list or card table that already exists in a project into a template, where it joins the [to-do list templates](template_library.md) or [card table templates](card_table_templates.md). Nothing else can be templatified. + +It runs in the background. The endpoint answers immediately with a templatification, which is then polled for its result. + +Templatify a to-do list or card table +------------------------------------- + +* `POST /buckets/1/recordings/2/templatifications.json` starts templatifying recording `2` into the template library. + +Every attribute is optional, and a request with no body at all is valid. An unnamed template takes the title of the recording it was made from. + +* `template_name` - what to call the template. Defaults to the source's own title. +* `copy_comments` - carry the comments across. Defaults to `false`. +* `copy_assignments` - carry assignees and the people involved across, adding them to the library if they aren't already there. Defaults to `false`. +* `move_cards_to_triage` - gather the cards into the Triage column instead of leaving them where they sit. **Card tables only**, and ignored for a to-do list. Defaults to `false`. + +Answers `201 Created` with the record described below. `403 Forbidden` if the recording isn't a to-do list or card table, if the caller is a client, or if the caller can't edit the project the work lives in. + +###### Example JSON Request + +```json +{ + "template_name": "Client onboarding", + "copy_assignments": true +} +``` + + +###### Example JSON Response + +```json +{ + "id": 3, + "status": "pending", + "source_recording_id": 1069479864, + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069479864/templatifications/3.json" +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"template_name":"Client onboarding","copy_assignments":true}' -X POST \ + https://3.basecampapi.com/$ACCOUNT_ID/buckets/1/recordings/2/templatifications.json +``` + +Get a templatification +---------------------- + +* `GET /buckets/1/recordings/2/templatifications/3.json` returns the current state of the templatification. + +`status` is `pending` or `processing` while the work is in progress, `completed` when the template is ready, and `failed` if it could not be made. Poll until it leaves `pending` and `processing`. + +A completed templatification carries the template it made, under `destination_todolist` or `destination_card_table` depending on the kind of recording it came from. These are the same keys, holding the same shapes, that [a template copy](template_library.md#get-a-template-copy) reports. + +Only the person who started the templatification can read it. Anyone else gets `404 Not Found`. + +###### Example JSON Response + +```json +{ + "id": 3, + "status": "completed", + "source_recording_id": 1069479864, + "url": "https://3.basecampapi.com/195539477/buckets/2085958504/recordings/1069479864/templatifications/3.json", + "destination_todolist": { + "id": 1069480319, + "status": "active", + "visible_to_clients": false, + "created_at": "2026-09-12T08:22:06.428Z", + "updated_at": "2026-09-12T08:22:06.708Z", + "title": "Client onboarding", + "inherits_status": true, + "type": "Todolist", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480319.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480319", + "bookmark_url": "https://3.basecampapi.com/195539477/my/bookmarks/BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiLmdpZDovL2JjMy9SZWNvcmRpbmcvMTA2OTQ4MDMxOT9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg1yZWFkYWJsZQY7AFQ=--ccc56c890ea4527b3c54a99f993c74f032b3e2be.json", + "comments_count": 0, + "comments_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480319/comments.json", + "boosts_count": 0, + "boosts_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480319/boosts.json", + "bubble_up_url": "https://3.basecampapi.com/195539477/buckets/2085958495/recordings/1069480319/bubble_up.json", + "position": 1, + "parent": { + "id": 1069478890, + "title": "To-do List Templates", + "type": "Todoset", + "url": "https://3.basecampapi.com/195539477/buckets/2085958495/todosets/1069478890.json", + "app_url": "https://3.basecamp.com/195539477/buckets/2085958495/todosets/1069478890" + }, + "bucket": { + "id": 2085958495, + "name": "To-do List Templates", + "type": "TemplateLibrary" + }, + "creator": { + "id": 1049715913, + "attachable_sgid": "BAh7BkkiC19yYWlscwY6BkVUewdJIglkYXRhBjsAVEkiK2dpZDovL2JjMy9QZXJzb24vMTA0OTcxNTkxMz9leHBpcmVzX2luBjsAVEkiCHB1cgY7AFRJIg9hdHRhY2hhYmxlBjsAVA==--e627c45e6b34e08862da23906862412620e4d5d9", + "name": "Victor Cooper", + "personable_type": "User", + "title": "Chief Strategist", + "tagline": "Don't let your dreams be dreams", + "location": "Chicago, IL", + "created_at": "2026-09-12T08:17:09.797Z", + "updated_at": "2026-09-12T08:17:10.372Z", + "email_address": "victor@honchodesign.com", + "bio": "Don't let your dreams be dreams", + "admin": true, + "owner": true, + "client": false, + "employee": true, + "time_zone": "America/Chicago", + "avatar_url": "https://3.basecampapi.com/195539477/people/BAhpBMlkkT4=--5fe7b70fbee7a7f0e2e1e19df7579e5d880c753d/avatar", + "company": { + "id": 1033447817, + "name": "Honcho Design" + }, + "can_ping": true, + "can_manage_projects": true, + "can_manage_people": true, + "can_access_timesheet": true, + "can_access_hill_charts": true + }, + "description": "", + "description_attachments": [], + "completed": false, + "completed_ratio": "0/5", + "name": "Client onboarding", + "color": null, + "groups_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480319/groups.json", + "todos_url": "https://3.basecampapi.com/195539477/buckets/2085958495/todolists/1069480319/todos.json", + "app_todos_url": "https://3.basecamp.com/195539477/buckets/2085958495/todolists/1069480319/todos", + "comments_app_url": "https://3.basecamp.com/195539477/buckets/2085958495/recordings/1069480319/comments" + } +} +``` + + +###### Copy as cURL + +```shell +curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ + https://3.basecampapi.com/$ACCOUNT_ID/buckets/1/recordings/2/templatifications/3.json +``` diff --git a/sections/timeline.md b/sections/timeline.md index 3debf4d..95a4d56 100644 --- a/sections/timeline.md +++ b/sections/timeline.md @@ -1154,7 +1154,8 @@ Each timeline event includes these fields: | `parent_recording_id` | ID of the parent container (e.g., to-do list for a to-do), if applicable | | `url` | API URL to fetch the item | | `app_url` | Link to view the item in Basecamp | -| `creator` | The [person][person] who performed the action | +| `creator` | The [person][person] the action is attributed to | +| `performed_by` | The agent that carried out the action on the creator's behalf, as a [person][person] with `"personable_type": "Agent"`. Present only for [delegated events](events.md#delegated-events) | | `action` | Human-readable description of the action | | `target` | The name of the parent container or item affected | | `title` | Combined action and target as a complete sentence |