├── README.md └── sections ├── accesses.md ├── attachments.md ├── authentication.md ├── calendar_events.md ├── calendars.md ├── comments.md ├── documents.md ├── events.md ├── forwards.md ├── groups.md ├── messages.md ├── people.md ├── project_templates.md ├── projects.md ├── stars.md ├── tags.md ├── todolists.md ├── todos.md ├── topics.md └── uploads.md /README.md: -------------------------------------------------------------------------------- 1 | The Basecamp 2 API 2 | ==================== 3 | 4 | Basecamp 2 has its own API. It is not compatible with the [Basecamp Classic API](https://github.com/basecamp/basecamp-classic-api) or the [Basecamp 3 API](https://github.com/basecamp/bc3-api). The core ingredients are still the same, though. This is a REST-style API that uses JSON for serialization and OAuth 2 for authentication. 5 | 6 | 7 | Making a request 8 | ---------------- 9 | 10 | All URLs start with `https://basecamp.com/999999999/api/v1/`. **SSL only**. The path is prefixed with the account id and the API version. If we change the API in backward-incompatible ways, we'll bump the version marker and maintain stable support for the old URLs. 11 | 12 | To make a request for all the projects on your account, you'd append the projects index path to the base url to form something like https://basecamp.com/999999999/api/v1/projects.json. In curl, that looks like: 13 | 14 | ```shell 15 | curl -u user:pass -H 'User-Agent: MyApp (yourname@example.com)' https://basecamp.com/999999999/api/v1/projects.json 16 | ``` 17 | 18 | To create something, it's the same deal except you also have to include the `Content-Type` header and the JSON data: 19 | 20 | ```shell 21 | curl -u username:password \ 22 | -H 'Content-Type: application/json' \ 23 | -H 'User-Agent: MyApp (yourname@example.com)' \ 24 | -d '{ "name": "My new project!" }' \ 25 | https://basecamp.com/999999999/api/v1/projects.json 26 | ``` 27 | 28 | That's all! 29 | 30 | 31 | Authentication 32 | -------------- 33 | 34 | If you're making a private integration with Basecamp for your own purposes, you can use HTTP Basic authentication. This is secure since all requests in the new Basecamp use SSL. 35 | 36 | If you're making a public integration with Basecamp for others to enjoy, you must use OAuth 2. This allows users to authorize your application to use Basecamp on their behalf without having to copy/paste API tokens or touch sensitive login info. 37 | 38 | Read the [authentication guide](https://github.com/basecamp/api/blob/master/sections/authentication.md) to get started. 39 | 40 | 41 | Identify your app 42 | ----------------- 43 | 44 | You must include a `User-Agent` header with the name of your application and a link to it or your email address so we can get in touch in case you're doing something wrong (so we may warn you before you're blacklisted) or something awesome (so we may congratulate you). Here's a couple of examples: 45 | 46 | User-Agent: Freshbooks (http://freshbooks.com/contact.php) 47 | User-Agent: Fabian's Ingenious Integration (fabian@example.com) 48 | 49 | If you don't supply this header, you will get a `400 Bad Request` response. 50 | 51 | 52 | No XML, just JSON 53 | ----------------- 54 | 55 | We only support JSON for serialization of data. Our format is to have no root element and we use snake\_case to describe attribute keys. This means that you have to send `Content-Type: application/json; charset=utf-8` when you're POSTing or PUTing data into Basecamp. **All API URLs end in .json to indicate that they accept and return JSON.** 56 | 57 | You'll receive a `415 Unsupported Media Type` response code if you attempt to use a different URL suffix or leave out the `Content-Type` header. 58 | 59 | Pagination 60 | ---------- 61 | 62 | Most collection APIs paginate their results. The first request returns up to 63 | 50 records. Check the next page for more results by adding `?page=2`, then 64 | `?page=3` (or `&page=2`, `&page=3`, if you already have a query string in the URL), 65 | and so on until you get an empty response. 66 | 67 | Use HTTP caching 68 | ---------------- 69 | 70 | You must make use of the HTTP freshness headers to lessen the load on our servers (and increase the speed of your application!). Most requests we return will include an `ETag` or `Last-Modified` header. When you first request a resource, store this value, and then submit them back to us on subsequent requests as `If-None-Match` and `If-Modified-Since`. If the resource hasn't changed, you'll see a `304 Not Modified` response, which saves you the time and bandwidth of sending something you already have. 71 | 72 | 73 | Handling errors 74 | --------------- 75 | 76 | If Basecamp is having trouble, you might see a 5xx error. `500` means that the app is entirely down, but you might also see `502 Bad Gateway`, `503 Service Unavailable`, or `504 Gateway Timeout`. It's your responsibility in all of these cases to retry your request later. 77 | 78 | 79 | Rate limiting 80 | ------------- 81 | 82 | You can perform up to 500 requests per 10 second period from the same IP address for the same account. If you exceed this limit, you'll get a [429 Too Many Requests](http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-4) response for subsequent requests. Check the `Retry-After` header to see how many seconds to wait before retrying the request. 83 | 84 | 85 | 86 | API ready for use 87 | ----------------- 88 | 89 | * [Projects](https://github.com/basecamp/bcx-api/blob/master/sections/projects.md) 90 | * [Project Templates](https://github.com/basecamp/bcx-api/blob/master/sections/project_templates.md) 91 | * [Stars](https://github.com/basecamp/bcx-api/blob/master/sections/stars.md) 92 | * [People](https://github.com/basecamp/bcx-api/blob/master/sections/people.md) 93 | * [Accesses](https://github.com/basecamp/bcx-api/blob/master/sections/accesses.md) 94 | * [Companies/Groups](https://github.com/basecamp/bcx-api/blob/master/sections/groups.md) 95 | * [Events](https://github.com/basecamp/bcx-api/blob/master/sections/events.md) 96 | * [Topics](https://github.com/basecamp/bcx-api/blob/master/sections/topics.md) 97 | * [Messages](https://github.com/basecamp/bcx-api/blob/master/sections/messages.md) 98 | * [Comments](https://github.com/basecamp/bcx-api/blob/master/sections/comments.md) 99 | * [Todo lists](https://github.com/basecamp/bcx-api/blob/master/sections/todolists.md) 100 | * [Todos](https://github.com/basecamp/bcx-api/blob/master/sections/todos.md) 101 | * [Documents](https://github.com/basecamp/bcx-api/blob/master/sections/documents.md) 102 | * [Attachments](https://github.com/basecamp/bcx-api/blob/master/sections/attachments.md) 103 | * [Uploads](https://github.com/basecamp/bcx-api/blob/master/sections/uploads.md) 104 | * [Calendars](https://github.com/basecamp/bcx-api/blob/master/sections/calendars.md) 105 | * [Calendar events](https://github.com/basecamp/bcx-api/blob/master/sections/calendar_events.md) 106 | * [Forwards](https://github.com/basecamp/bcx-api/blob/master/sections/forwards.md) 107 | 108 | API libraries 109 | ------------- 110 | 111 | * [Logan](https://rubygems.org/gems/logan) - Ruby gem 112 | * [bcx.rb](http://paulspringett.github.io/bcx/docs/bcx.html) - Ruby client 113 | 114 | Help us make it better 115 | ---------------------- 116 | 117 | Please tell us how we can make the API better. If you have a specific feature request or if you found a bug, please use GitHub issues. Fork these docs and send a pull request with improvements. 118 | 119 | To talk with us and other developers about the API, [post a question on StackOverflow](http://stackoverflow.com/questions/ask) tagged `basecamp` or [open a support ticket](https://basecamp.com/support). 120 | -------------------------------------------------------------------------------- /sections/accesses.md: -------------------------------------------------------------------------------- 1 | Accesses 2 | ======== 3 | 4 | Get accesses 5 | ------------ 6 | 7 | * `GET /projects/1/accesses.json` will return all the people with access to the project. 8 | * `GET /calendars/1/accesses.json` will return all the people with access to the calendar. 9 | 10 | We will return 50 people per page. If the response has 50 people, check the next page 11 | for more people. Do this by adding `&page=2` to the query, then `&page=3` and so on. 12 | 13 | ```json 14 | [ 15 | { 16 | "id": 149087659, 17 | "identity_id": 982871737, 18 | "name": "Jason Fried", 19 | "email_address": "jason@basecamp.com", 20 | "admin": true, 21 | "is_client": false, 22 | "trashed": false, 23 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif", 24 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 25 | "created_at": "2012-03-22T16:56:48-05:00", 26 | "updated_at": "2012-03-22T16:56:48-05:00", 27 | "url": "https://basecamp.com/999999999/api/v1/people/149087659-jason-fried.json", 28 | "app_url": "https://basecamp.com/999999999/people/149087659-jason-fried" 29 | }, 30 | { 31 | "id": 1071630348, 32 | "identity_id": 827377171, 33 | "name": "Jeremy Kemper", 34 | "email_address": "jeremy@basecamp.com", 35 | "admin": true, 36 | "is_client": false, 37 | "trashed": false, 38 | "avatar_url": "https://asset0.37img.com/global/e68cafa694e8f22203eb36f13dccfefa9ac0acb2/avatar.96.gif", 39 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 40 | "created_at": "2012-03-22T16:56:48-05:00", 41 | "updated_at": "2012-03-22T16:56:48-05:00", 42 | "url": "https://basecamp.com/999999999/api/v1/people/1071630348-jeremy-kemper.json", 43 | "app_url": "https://basecamp.com/999999999/people/1071630348-jeremy-kemper" 44 | } 45 | ] 46 | ``` 47 | 48 | Grant access 49 | ------------ 50 | 51 | Grant access to team members: 52 | 53 | * `POST /projects/1/accesses.json` will grant team access to the project for the existing `ids` of people already on the account or new people via `email_addresses`. (Same goes for calendars with /calendars/ instead) 54 | 55 | ```json 56 | { 57 | "ids": [ 5, 6, 10 ], 58 | "email_addresses": [ "someone@example.com", "someoneelse@example.com" ] 59 | } 60 | ``` 61 | 62 | Grant access to clients: 63 | 64 | * `POST /projects/1/client_accesses.json` will grant client access to the project for the existing `ids` of people already on the account or new people via `email_addresses`. Calendars only have team access so this endpoint is not applicable. Project calendars adhere to the accesses set in the project. 65 | 66 | ```json 67 | { 68 | "ids": [ 1, 9, 11 ], 69 | "email_addresses": [ "client@example.com", "anotherclient@example.com" ] 70 | } 71 | ``` 72 | 73 | You can get the ids of existing people on the account from the [people API](https://github.com/basecamp/bcx-api/blob/master/sections/people.md). 74 | 75 | This will return `204 No Content` if the access was granted successfully. If the authenticated user does not have access to this project, `404 Not Found` will be returned. 76 | 77 | 78 | Revoke access 79 | ------------- 80 | 81 | * `DELETE /projects/1/accesses/1.json` will revoke the access of the person who's id is mentioned in the URL. (Same goes for calendars with /calendars/ instead) 82 | 83 | This will return `204 No Content` if the revoke was a success. If the user does not have access to revoke access from the project, `403 Forbidden` will be returned. If the authenticated user does not have access to this project, `404 Not Found` will be returned. 84 | -------------------------------------------------------------------------------- /sections/attachments.md: -------------------------------------------------------------------------------- 1 | Attachments 2 | =========== 3 | 4 | Uploading files to Basecamp is a two-step process: 5 | 6 | 1. Create the attachment and receive a token verifying that the upload was successful. 7 | 2. Associate the attachment with a message, to-do, upload, or comment. See the following endpoints for attaching: 8 | 9 | * [Create messages](https://github.com/basecamp/bcx-api/blob/master/sections/messages.md) 10 | * [Create to-dos](https://github.com/basecamp/bcx-api/blob/master/sections/todos.md) 11 | * [Create uploads](https://github.com/basecamp/bcx-api/blob/master/sections/uploads.md) 12 | * [Create comments](https://github.com/basecamp/bcx-api/blob/master/sections/comments.md) 13 | 14 | 15 | Create attachment 16 | ----------------- 17 | 18 | * `POST /attachments.json` uploads a file. The request body should be the file's binary data. The `Content-Type` and `Content-Length` headers should be set accordingly. 19 | 20 | When an upload is successful, you'll get a `200 OK` response, with a token that you use to attach the file to something. 21 | 22 | ```json 23 | { 24 | "token": "4f71ea23-134660425d1818169ecfdbaa43cfc07f4e33ef4c" 25 | } 26 | ``` 27 | 28 | Here's an example using `curl`: 29 | 30 | ``` 31 | curl --data-binary @logo.png \ 32 | -u user:pass \ 33 | -H 'Content-Type: image/png' \ 34 | -H 'User-Agent: Rapp (david@basecamp.com)' \ 35 | https://basecamp.com/999999999/api/v1/attachments.json 36 | ``` 37 | 38 | **Note:** If a file is big, uploading can take a long time! Make sure to account for this in your implementation. 39 | 40 | 41 | Get attachment 42 | -------------- 43 | 44 | * `GET /projects/1/attachments/1.json` will return the specified attachment with its file metadata, URLs, and associated attachable (a message, to-do, upload, or comment). 45 | 46 | ```json 47 | { 48 | "id": 999008202, 49 | "key": "40b8a84cb1a30dbe04457dc99e094b6299deea41", 50 | "name": "bearwave.gif", 51 | "byte_size": 508254, 52 | "content_type": "image/gif", 53 | "created_at": "2012-03-27T22:48:49-04:00", 54 | "updated_at": "2012-03-27T22:48:49-04:00", 55 | "url": "https://asset1.basecamp.com/1111/api/v1/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 56 | "app_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 57 | "thumbnail_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/thumbnail.gif", 58 | "private": false, 59 | "trashed": false, 60 | "tags": ["favorite gifs"], 61 | "creator": { 62 | "id": 73, 63 | "name": "Nick Quaranto", 64 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 65 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 66 | }, 67 | "attachable": { 68 | "id": 70219655, 69 | "type": "Upload", 70 | "url": "https://basecamp.com/1111/api/v1/projects/2222/uploads/70219655.json", 71 | "app_url": "https://basecamp.com/1111/projects/2222/uploads/70219655" 72 | } 73 | } 74 | ``` 75 | 76 | 77 | Get attachments 78 | --------------- 79 | 80 | * `GET /projects/1/attachments.json` returns attachments in the specified project, each with its file metadata, URLs, and associated attachable (a message, to-do, upload, or comment). 81 | * `GET /attachments.json` returns attachments in all projects. 82 | 83 | ```json 84 | [ 85 | { 86 | "id": 999008202, 87 | "key": "40b8a84cb1a30dbe04457dc99e094b6299deea41", 88 | "name": "bearwave.gif", 89 | "byte_size": 508254, 90 | "content_type": "image/gif", 91 | "created_at": "2012-03-27T22:48:49-04:00", 92 | "updated_at": "2012-03-27T22:48:49-04:00", 93 | "url": "https://asset1.basecamp.com/1111/api/v1/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 94 | "app_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 95 | "thumbnail_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/thumbnail.gif", 96 | "private": false, 97 | "trashed": false, 98 | "tags": ["favorite gifs"], 99 | "creator": { 100 | "id": 73, 101 | "name": "Nick Quaranto", 102 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 103 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 104 | }, 105 | "attachable": { 106 | "id": 70219655, 107 | "type": "Upload", 108 | "url": "https://basecamp.com/1111/api/v1/projects/2222/uploads/70219655.json", 109 | "app_url": "https://basecamp.com/1111/projects/2222/uploads/70219655" 110 | } 111 | } 112 | { 113 | "id": 999008203, 114 | "key": "773c74212f81f5c7d66917fb7236d5aece36c56a", 115 | "name": "report.pdf", 116 | "byte_size": 508254, 117 | "content_type": "application/pdf", 118 | "created_at": "2012-03-27T22:48:49-04:00", 119 | "updated_at": "2012-03-27T22:48:49-04:00", 120 | "url": "https://asset1.basecamp.com/1111/api/v1/projects/2222/attachments/4444/773c74212f81f5c7d66917fb7236d5aece36c56a/original/report.pdf", 121 | "app_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/4444/773c74212f81f5c7d66917fb7236d5aece36c56a/original/report.pdf", 122 | "thumbnail_url": "https://asset1.basecamp.com/1111/projects/2222/attachments/4444/773c74212f81f5c7d66917fb7236d5aece36c56a/thumbnail.png", 123 | "private": false, 124 | "trashed": false, 125 | "tags": ["reports"], 126 | "creator": { 127 | "id": 73, 128 | "name": "Nick Quaranto", 129 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 130 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 131 | }, 132 | "attachable": { 133 | "id": 12092382, 134 | "type": "Message", 135 | "url": "https://basecamp.com/1111/api/v1/projects/2222/messages/12092382.json", 136 | "app_url": "https://basecamp.com/1111/projects/2222/messages/12092382" 137 | } 138 | } 139 | ] 140 | ``` 141 | 142 | Linked attachments like [Google Docs](https://basecamp.com/help/guides/projects/google-docs) don't have a `url` attribute and include additional attributes about the source: 143 | 144 | ```json 145 | { 146 | "id": 999008204, 147 | "name": "Business ponderings", 148 | "byte_size": 0, 149 | "content_type": "application/vnd.google-apps.document", 150 | "linked_source": "google", 151 | "linked_type": "document", 152 | "link_url": "https://docs.google.com/document/d/1fNihMDicThD....", 153 | "created_at": "2012-03-28T22:48:49-04:00", 154 | "updated_at": "2012-03-28T22:48:49-04:00", 155 | "private": false, 156 | "trashed": false, 157 | "tags": ["writing"], 158 | "creator": { 159 | "id": 73, 160 | "name": "Nick Quaranto", 161 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 162 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 163 | }, 164 | "attachable": { 165 | "id": 12092383, 166 | "type": "Message", 167 | "url": "https://basecamp.com/1111/api/v1/projects/2222/messages/12092383.json", 168 | "app_url": "https://basecamp.com/1111/projects/2222/messages/12092383" 169 | } 170 | } 171 | ``` 172 | 173 | If you need more information about what an attachment is attached to, you can 174 | make another request to its `attachable`'s `url` value. 175 | 176 | ### Pagination 177 | 178 | We will return 50 attachments per page. If the result set has 50 attachments, 179 | it's your responsibility to check the next page to see if there are any more 180 | attachments -- you do this by adding `&page=2` to the query, then `&page=3`, and so on. 181 | 182 | ### Sorting 183 | 184 | It's also possible to change the order attachments are returned in with the `sort` 185 | parameter. Attachments can be sorted by name, size, or age using the parameter 186 | values: 187 | 188 | * `az` and `za` for name 189 | * `biggest` and `smallest` for size 190 | * `newest` and `oldest` for age 191 | 192 | The default sort is `newest`. 193 | 194 | Sorting can be combined with pagination. To get the second page of attachments 195 | sorted by oldest first, request `/attachments.json?page=2&sort=oldest`. 196 | 197 | 198 | Rename attachment 199 | ----------------- 200 | 201 | * `PUT /projects/1/attachments/1.json` will rename the specified attachment. 202 | 203 | ```json 204 | {"name": "TPS report.pdf"} 205 | ``` 206 | 207 | This will return `200 OK` if the update was a success, with the current JSON 208 | representation of the attachment in the response body. If the user does not 209 | have permission to update the attachment, you'll receive `403 Forbidden`. 210 | 211 | Linked attachments, such as [Google Docs](https://basecamp.com/help/guides/projects/google-docs), 212 | can't be renamed. Their names are automatically synced from their sources when 213 | they are viewed. If you attempt to rename a linked attachment, you'll receive 214 | an error with a `400 Bad Request` response status. 215 | 216 | 217 | Delete attachment 218 | ----------------- 219 | 220 | * `DELETE /projects/1/attachments/1.json` will delete the attachment specified and return `204 No Content` if that was successful. If the user does not have access to delete the attachment, you'll see `403 Forbidden`. 221 | 222 | If an attachment on an upload with no comments is deleted, the upload will be deleted as well. If an attachment on an upload with comments is deleted, the upload will remain. 223 | 224 | 225 | Private attachments 226 | ------------------- 227 | 228 | Attachments inherit the privacy of their containers. For example, an attachment 229 | on a private message is private. Making the message visible to clients will 230 | make all of its attachments visible to clients. 231 | -------------------------------------------------------------------------------- /sections/authentication.md: -------------------------------------------------------------------------------- 1 | Authentication Guide Moved 2 | ========================== 3 | 4 | The authentication guide for the all new Basecamp, along with other Basecamp products, is now here: 5 | 6 | https://github.com/basecamp/api/blob/master/sections/authentication.md 7 | -------------------------------------------------------------------------------- /sections/calendar_events.md: -------------------------------------------------------------------------------- 1 | Calendar events 2 | =============== 3 | 4 | Calendar events are entries on the calendar -- not to be confused with "events", which track all activity in Basecamp. A calendar event can belong to a project or to a standalone calendar. 5 | 6 | If a calendar event is an all day affair, its `starts_at` and `ends_at` values will be dates. For a timed calendar event, the `starts_at` and `ends_at` values are times with timezones. 7 | 8 | 9 | Get calendar events 10 | ------------------- 11 | 12 | * `GET /projects/1/calendar_events.json` will return upcoming calendar events for the project. 13 | * `GET /calendars/1/calendar_events.json` will return upcoming calendar events for the calendar. 14 | * `GET /projects/1/calendar_events/past.json` will return past calendar events for the project. 15 | * `GET /calendars/1/calendar_events/past.json` will return past calendar events for the calendar. 16 | 17 | ```json 18 | [ 19 | { 20 | "id": 883432030, 21 | "summary": "something coming up", 22 | "description": "", 23 | "created_at": "2012-03-28T11:50:00-05:00", 24 | "updated_at": "2012-03-28T12:24:59-05:00", 25 | "all_day": false, 26 | "starts_at": "2012-03-28T07:00:00-05:00", 27 | "ends_at": "2012-03-28T07:00:00-05:00", 28 | "comments_count": 0, 29 | "private": false, 30 | "trashed": false, 31 | "creator": { 32 | "id": 149087659, 33 | "name": "Jason Fried", 34 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 35 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 36 | }, 37 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/883432030.json", 38 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/883432030" 39 | }, 40 | { 41 | "id": 883432031, 42 | "summary": "More stuff for later", 43 | "description": "Details will follow", 44 | "created_at": "2012-03-28T12:29:16-05:00", 45 | "updated_at": "2012-03-28T12:29:16-05:00", 46 | "all_day": true, 47 | "starts_at": "2012-03-28", 48 | "ends_at": "2012-03-28", 49 | "comments_count": 0, 50 | "private": false, 51 | "trashed": false, 52 | "creator": { 53 | "id": 149087659, 54 | "name": "Jason Fried", 55 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 56 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 57 | }, 58 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/883432031.json", 59 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/883432031" 60 | }, 61 | { 62 | "id": 1030049109, 63 | "summary": "Weekly meeting", 64 | "description": "To discuss business", 65 | "created_at": "2014-07-09T09:40:33.000-05:00", 66 | "updated_at": "2014-07-09T09:40:33.000-05:00", 67 | "all_day": true, 68 | "starts_at": "2014-07-10", 69 | "ends_at": "2014-07-10", 70 | "comments_count": 0, 71 | "private": false, 72 | "trashed": false, 73 | "creator": { 74 | "id": 149087659, 75 | "name": "Jason Fried", 76 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 77 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 78 | }, 79 | "recurring": { 80 | "frequency": "weekly", 81 | "count": null, 82 | "until": null, 83 | "excluding": [2, 3] 84 | }, 85 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 86 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 87 | } 88 | ] 89 | ``` 90 | 91 | * `GET /calendar_events.json?start_date=2014-07-10` will return six weeks of calendar events after the start date for the account, including recurrences. 92 | * `GET /projects/1/calendar_events.json?start_date=2014-07-10` will return six weeks of calendar events after the start date for the project, including recurrences. 93 | * `GET /calendars/1/calendar_events.json?start_date=2014-07-10` will return six weeks of calendar events after the start date for the calendar, including recurrences. 94 | * `GET /calendar_events.json?start_date=2014-07-10&end_date=2014-07-12` will return calendar events between the start and end date for the account, including recurrences. The start and end date can be up to six weeks apart. 95 | * `GET /projects/1/calendar_events.json?start_date=2014-07-10&end_date=2014-07-12` will return calendar events between the start and end date for the project, including recurrences. The start and end date can be up to six weeks apart. 96 | * `GET /calendars/1/calendar_events.json?start_date=2014-07-10&end_date=2014-07-12` will return calendar events between the start and end date for the calendar, including recurrences. The start and end date can be up to six weeks apart. 97 | 98 | ```json 99 | [ 100 | { 101 | "id": 1030049109, 102 | "summary": "Weekly meeting", 103 | "description": "To discuss business", 104 | "created_at": "2014-07-10T09:40:33.000-05:00", 105 | "updated_at": "2014-07-10T09:40:33.000-05:00", 106 | "all_day": true, 107 | "starts_at": "2014-07-10", 108 | "ends_at": "2014-07-10", 109 | "comments_count": 0, 110 | "private": false, 111 | "trashed": false, 112 | "creator": { 113 | "id": 149087659, 114 | "name": "Jason Fried", 115 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 116 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 117 | }, 118 | "recurring": { 119 | "frequency": "weekly", 120 | "count": 5, 121 | "until": null, 122 | "excluding": [2, 3] 123 | }, 124 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 125 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 126 | }, 127 | { 128 | "summary": "Weekly meeting", 129 | "description": "To discuss business", 130 | "all_day": true, 131 | "starts_at": "2014-07-10", 132 | "ends_at": "2014-07-10", 133 | "private": false, 134 | "trashed": false, 135 | "creator": { 136 | "id": 149087659, 137 | "name": "Jason Fried", 138 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 139 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 140 | }, 141 | "recurrence": { 142 | "number": 1, 143 | "master": { 144 | "id": 1030049109, 145 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 146 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 147 | } 148 | } 149 | }, 150 | { 151 | "summary": "Weekly meeting", 152 | "description": "To discuss business", 153 | "all_day": true, 154 | "starts_at": "2014-07-10", 155 | "ends_at": "2014-07-10", 156 | "comments_count": 0, 157 | "private": false, 158 | "trashed": false, 159 | "creator": { 160 | "id": 149087659, 161 | "name": "Jason Fried", 162 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 163 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 164 | }, 165 | "recurrence": { 166 | "number": 4, 167 | "master": { 168 | "id": 1030049109, 169 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 170 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 171 | } 172 | } 173 | } 174 | ] 175 | ``` 176 | 177 | See [Recurring calendar events](#recurring-calendar-events) for more information. 178 | 179 | Endpoints that include recurrences are paginated within the specified window and return 50 calendar events per page. It is your responsibility to check if the next page contains more calendar events. You do this by specifying a page parameter -- `/calendar_events.json?start_date=2014-07-10&page=2`, `/calendar_events.json?start_date=2014-07-10&page=3`, and so on. 180 | 181 | 182 | Get calendar event 183 | ------------------ 184 | 185 | * `GET /projects/1/calendar_events/1.json` will return the specified calendar event. 186 | * `GET /calendars/1/calendar_events/1.json` will return the specified calendar event. 187 | 188 | ```json 189 | { 190 | "id": 883432030, 191 | "summary": "something coming up", 192 | "description": "", 193 | "created_at": "2012-03-28T11:50:00-05:00", 194 | "updated_at": "2012-03-28T12:24:59-05:00", 195 | "all_day": false, 196 | "starts_at": "2012-03-28T07:00:00-05:00", 197 | "ends_at": "2012-03-28T07:00:00-05:00", 198 | "private": false, 199 | "trashed": false, 200 | "creator": { 201 | "id": 149087659, 202 | "name": "Jason Fried", 203 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 204 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 205 | }, 206 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/883432030.json", 207 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/883432030", 208 | "comments": [ 209 | { 210 | "id": 1028592772, 211 | "content": "let's get it taken care of?", 212 | "created_at": "2012-03-28T12:24:59-05:00", 213 | "updated_at": "2012-03-28T12:24:59-05:00", 214 | "attachments": [], 215 | "creator": { 216 | "id": 149087659, 217 | "name": "Jason Fried", 218 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 219 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 220 | } 221 | } 222 | ], 223 | "subscribers": [ 224 | { 225 | "id": 149087659, 226 | "name": "Jason Fried" 227 | }, 228 | { 229 | "id": 1071630348, 230 | "name": "Jeremy Kemper" 231 | } 232 | ] 233 | } 234 | 235 | ``` 236 | 237 | 238 | Create calendar event 239 | --------------------- 240 | 241 | * `POST /projects/1/calendar_events.json` will create a new calendar event for a project. 242 | * `POST /calendars/1/calendar_events.json` will create a new calendar event for a calendar. 243 | 244 | Examples: 245 | 246 | ```json 247 | { 248 | "summary": "My single, all-day event", 249 | "description": "Details to follow", 250 | "all_day": true, 251 | "starts_at": "2012-03-28" 252 | } 253 | ``` 254 | 255 | ```json 256 | { 257 | "summary": "My all-day event spanning two days", 258 | "description": "Details to follow", 259 | "all_day": true, 260 | "starts_at": "2012-03-28", 261 | "ends_at": "2012-03-30" 262 | } 263 | ``` 264 | 265 | ```json 266 | { 267 | "summary": "My single event for a specific time", 268 | "description": "Details to follow", 269 | "starts_at": "2012-03-28T11:50:00-05:00" 270 | } 271 | ``` 272 | 273 | ```json 274 | { 275 | "summary": "My timed event with a reminder", 276 | "description": "For an email reminder, set remind_at", 277 | "starts_at": "2012-03-28T11:50:00-05:00", 278 | "remind_at": "2012-03-28T11:20:00-05:00" 279 | } 280 | ``` 281 | To subscribe specific people to a calendar event pass their person IDs with the create parameters. 282 | 283 | ```json 284 | { 285 | "summary": "My single event for a specific time", 286 | "description": "Details to follow", 287 | "starts_at": "2012-03-28T11:50:00-05:00", 288 | "subscribers": [ 289 | 149087659, 290 | 1071630348 291 | ] 292 | } 293 | ``` 294 | 295 | To subscribe all people on the project to a calendar event pass "all" with the create parameters. 296 | 297 | ```json 298 | { 299 | "summary": "My single event for a specific time", 300 | "description": "Details to follow", 301 | "starts_at": "2012-03-28T11:50:00-05:00", 302 | "subscribers": "all" 303 | } 304 | ``` 305 | 306 | This will return `201 Created`, with the URL of the new calendar_event in the `Location` header and a JSON representation of the event in the response body, if the creation was a success. If the dates are not in the proper format, you'll get a `400 Bad Request`. 307 | 308 | Basecamp will send a reminder email to all subscribers if `remind_at` is set. `remind_at` can't be more than three days before `starts_at` or any time after `starts_at`. 309 | 310 | See [Create a recurring event](#create-a-recurring-event) for examples of recurring events. 311 | 312 | Update calendar event 313 | --------------------- 314 | 315 | * `PUT /projects/1/calendar_events/1.json` will update the specific calendar event on a project. 316 | * `PUT /calendars/1/calendar_events/1.json` will update the specific calendar event on a calendar. 317 | 318 | ```json 319 | { 320 | "summary": "My all-day event spanning two days", 321 | "description": "Details to follow", 322 | "all_day": true, 323 | "starts_at": "2012-03-28", 324 | "ends_at": "2012-03-30" 325 | } 326 | ``` 327 | To update the subscribers on an event pass their person IDs with the parameters. Note: This update will replace all subscribers, not add subscribers to the calendar event. 328 | 329 | ```json 330 | { 331 | "subscribers": [ 332 | 149087659, 333 | 1071630348 334 | ] 335 | } 336 | ``` 337 | 338 | To subscribe all people to a calendar event, pass "all" with the parameters. 339 | 340 | ```json 341 | { 342 | "subscribers": "all" 343 | } 344 | ``` 345 | 346 | To remove all subscribers on an event pass an empty array with the parameters. 347 | 348 | ```json 349 | { 350 | "subscribers": [] 351 | } 352 | ``` 353 | 354 | This will return `200 OK` if the creation was a success, with a JSON representation of the resource in the response body. If the dates are not in the proper format, you'll get a `400 Bad Request`. 355 | 356 | Delete calendar event 357 | --------------------- 358 | 359 | * `DELETE /projects/1/calendar_events/1.json` will delete the calendar event specified and return `204 No Content` if that was successful. (The same for /calendars/) 360 | 361 | 362 | Recurring calendar events 363 | ------------------------- 364 | 365 | For recurring events: 366 | 367 | * The `frequency` value may be `"daily"`, `"weekly"`, `"monthly"`, or `"yearly"`. 368 | * The `count` value describes how many times the event occurs. 369 | * The `until` value specifies the date of the last recurrence. 370 | * The `count` and `until` values are exclusive -- no event has both. 371 | * If `count` and `until` are both `null` for an event, it recurs infinitely. 372 | * The `excluding` value identifies which recurrences, indexed from 1, are skipped. 373 | 374 | Here's a sample recurring event: 375 | 376 | ```json 377 | { 378 | "id": 1030049109, 379 | "summary": "Weekly meeting", 380 | "description": "To discuss business", 381 | "created_at": "2014-07-10T09:40:33.000-05:00", 382 | "updated_at": "2014-07-10T09:40:33.000-05:00", 383 | "all_day": true, 384 | "starts_at": "2014-07-10", 385 | "ends_at": "2014-07-10", 386 | "private": false, 387 | "trashed": false, 388 | "creator": { 389 | "id": 149087659, 390 | "name": "Jason Fried", 391 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 392 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 393 | }, 394 | "recurring": { 395 | "frequency": "weekly", 396 | "count": 5, 397 | "until": null, 398 | "excluding": [2, 3] 399 | }, 400 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 401 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 402 | } 403 | ``` 404 | 405 | For recurrences of an original event, the `number` value specifies which recurrence is represented. Recurrences are numbered starting from 1. The first recurrence of an event is numbered 1, the second is 2, and so forth. A recurrence includes details about its originating event, such as its ID and API URL. Recurrences do not have their own IDs or URLs. 406 | 407 | Here's a sample recurrence: 408 | 409 | ```json 410 | { 411 | "summary": "Weekly meeting", 412 | "description": "To discuss business", 413 | "all_day": true, 414 | "starts_at": "2014-07-10", 415 | "ends_at": "2014-07-10", 416 | "private": false, 417 | "trashed": false, 418 | "creator": { 419 | "id": 149087659, 420 | "name": "Jason Fried", 421 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 422 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 423 | }, 424 | "recurrence": { 425 | "number": 1, 426 | "master": { 427 | "id": 1030049109, 428 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/1030049109.json", 429 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/1030049109" 430 | } 431 | } 432 | } 433 | ``` 434 | 435 | Create a recurring event 436 | ------------------------ 437 | * `POST /projects/1/calendar_events.json` will create a new calendar event for a project. 438 | * `POST /calendars/1/calendar_events.json` will create a new calendar event for a calendar. 439 | 440 | Examples: 441 | 442 | Use `count`: 443 | ```json 444 | { 445 | "summary": "My weekly all-day event", 446 | "description": "Details to follow", 447 | "all_day": true, 448 | "recurring": { 449 | "starts_at": "2012-03-28", 450 | "frequency": "weekly", 451 | "count": 4 452 | } 453 | } 454 | ``` 455 | or `until`: 456 | ```json 457 | { 458 | "summary": "My daily all-day event", 459 | "description": "Details to follow", 460 | "all_day": true, 461 | "recurring": { 462 | "starts_at": "2012-03-28", 463 | "frequency": "daily", 464 | "until": "2012-04-10", 465 | "excluding": [2,3] 466 | } 467 | } 468 | ``` 469 | 470 | or neither for an infinitely recurring event: 471 | 472 | ```json 473 | { 474 | "summary": "Happy birthday Basecamp!", 475 | "description": "Another year older", 476 | "all_day": true, 477 | "recurring": { 478 | "starts_at": "2004-02-04", 479 | "frequency": "yearly" 480 | } 481 | } 482 | ``` 483 | 484 | Private calendar events 485 | ----------------------- 486 | 487 | To hide a calendar event on a project from clients, set its `private` attribute to `true`. 488 | 489 | ```json 490 | { 491 | "summary": "My timed event with a reminder", 492 | "description": "For an email reminder, set remind_at", 493 | "starts_at": "2012-03-28T11:50:00-05:00", 494 | "remind_at": "2012-03-28T11:20:00-05:00", 495 | "private": true 496 | } 497 | ``` 498 | 499 | Calendars can't have clients, so you can't make events on calendars private. Attempting to do so will result in an error and a `422 Unprocessable Entity` response status. 500 | 501 | To reveal a calendar event to clients, set its `private` attribute to false. 502 | 503 | Comments on a calendar event inherit its privacy. If a calendar event is made public or private, so are all of its comments. 504 | -------------------------------------------------------------------------------- /sections/calendars.md: -------------------------------------------------------------------------------- 1 | Calendars 2 | ========= 3 | 4 | Get calendars 5 | ------------- 6 | 7 | * `GET /calendars.json` will return all calendars sorted alphabetically. 8 | 9 | ```json 10 | [ 11 | { 12 | "id": 336154974, 13 | "name": "Board Meetings", 14 | "updated_at": "2012-03-27T13:19:29-05:00", 15 | "color": "3185c5", 16 | "url": "https://basecamp.com/999999999/api/v1/calendars/336154974-board-meetings.json", 17 | "app_url": "https://basecamp.com/999999999/calendars/336154974-board-meetings" 18 | }, 19 | { 20 | "id": 237581901, 21 | "name": "General", 22 | "updated_at": "2012-03-27T13:19:29-05:00", 23 | "color": "3185c5", 24 | "url": "https://basecamp.com/999999999/api/v1/calendars/237581901-general.json", 25 | "app_url": "https://basecamp.com/999999999/calendars/237581901-general" 26 | } 27 | ] 28 | ``` 29 | 30 | 31 | Get calendar 32 | ------------ 33 | 34 | * `GET /calendars/1.json` will return the specified calendar. 35 | 36 | ```json 37 | { 38 | "id": 567469885, 39 | "name": "Vacation", 40 | "created_at": "2012-03-28T13:14:30-05:00", 41 | "updated_at": "2012-03-28T13:26:07-05:00", 42 | "color": "3185c5", 43 | "creator": { 44 | "id": 149087659, 45 | "name": "Jason Fried", 46 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 47 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 48 | }, 49 | "accesses": { 50 | "count": 3, 51 | "updated_at": "2012-03-28T13:14:31-05:00", 52 | "url": "https://basecamp.com/999999999/api/v1/calendars/567469885-vacation/accesses.json", 53 | "app_url": "https://basecamp.com/999999999/calendars/567469885-vacation/accesses" 54 | }, 55 | "calendar_events": { 56 | "count": 1, 57 | "updated_at": "2012-03-28T13:26:07-05:00", 58 | "urls": { 59 | "upcoming": "https://basecamp.com/999999999/api/v1/calendars/567469885-vacation/calendar_events.json", 60 | "past": "https://basecamp.com/999999999/api/v1/calendars/567469885-vacation/calendar_events/past.json" 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | 67 | Create calendar 68 | --------------- 69 | 70 | * `POST /calendars.json` will create a new calendar from the parameters passed. 71 | 72 | ```json 73 | { 74 | "name": "This is my new calendar!" 75 | } 76 | ``` 77 | 78 | This will return `201 Created`, with the location of the new calendar in the `Location` header along with a representation of the calendar in JSON in the response body if the creation was a success (See the **Get calendar** endpoint). 79 | 80 | 81 | Update calendar 82 | --------------- 83 | 84 | * `PUT /calendars/1.json` will update the calendar from the parameters passed. 85 | 86 | ```json 87 | { 88 | "name": "This is a new name for the calendar!" 89 | } 90 | ``` 91 | 92 | This will return `200 OK` if the update was a success, along with a representation of the calendar in JSON (See the **Get calendar** endpoint). If the user does not have access to update the calendar, you'll see `403 Forbidden`. 93 | 94 | 95 | Delete calendar 96 | --------------- 97 | 98 | * `DELETE /calendars/1.json` will delete the calendar specified and return `204 No Content` if that was successful. If the user does not have access to delete the calendar, you'll see `403 Forbidden`. 99 | -------------------------------------------------------------------------------- /sections/comments.md: -------------------------------------------------------------------------------- 1 | Comments 2 | ======== 3 | 4 | > HATERS GONNA HATE 5 | 6 | 7 | Get comments 8 | ------------ 9 | 10 | Comments are included on the [topics](https://github.com/basecamp/bcx-api/blob/master/sections/topics.md) directly. So to see all comments for a message, you'd just GET that message and they're included and look like this: 11 | 12 | ```json 13 | { 14 | "comments": [ 15 | { 16 | "id": 1028592764, 17 | "content": "Yeah, really, welcome!", 18 | "created_at": "2012-03-22T16:56:48-05:00", 19 | "updated_at": "2012-03-22T16:56:48-05:00", 20 | "private": false, 21 | "trashed": false, 22 | "attachments":[ 23 | { 24 | "key": "40b8a84cb1a30dbe04457dc99e094b6299deea41", 25 | "name": "bearwave.gif", 26 | "byte_size": 508254, 27 | "content_type":"image/png", 28 | "created_at":"2012-03-27T22:48:49-04:00", 29 | "url":"https://basecamp.com/1111/api/v1/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 30 | "app_url":"https://basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 31 | "creator":{ 32 | "id": 73, 33 | "name": "Nick Quaranto", 34 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 35 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 36 | } 37 | } 38 | ], 39 | "creator": { 40 | "id": 149087659, 41 | "name": "Jason Fried", 42 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 43 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 44 | } 45 | } 46 | ] 47 | } 48 | ``` 49 | 50 | 51 | Create comment 52 | -------------- 53 | 54 | * `POST /projects/1/
/1/comments.json` will create a new comment from the parameters passed for the commentable described via
/ -- for example /projects/1/messages/1/comments.json or /projects/1/todos/1/comments.json. The subscribers array is an optional list of people IDs that you want to notify about this comment (see [Get accesses](https://github.com/basecamp/bcx-api/blob/master/sections/accesses.md) on how to get the people IDs for a given project). 55 | 56 | ```json 57 | { 58 | "content": "Imma let you finish, but...", 59 | "subscribers": [1, 5, 6] 60 | } 61 | ``` 62 | 63 | To "loop-in" outside email addresses for users without an account, include a new_subscriber_emails array with the parameters. 64 | 65 | ```json 66 | { 67 | "subject": "Hello everyone", 68 | "content": "This is going to be a GREAT Saturday!", 69 | "new_subscriber_emails": ["example@example.com"] 70 | } 71 | ``` 72 | 73 | This will return `201 Created`, with a representation of the comment just created in the response body if the creation was a success. The topic can be accessed via the `topic_url` parameter. For example: 74 | 75 | ```json 76 | { 77 | "id": 1028592764, 78 | "content": "Yeah, really, welcome!", 79 | "created_at": "2012-03-22T16:56:48-05:00", 80 | "updated_at": "2012-03-22T16:56:48-05:00", 81 | "private": false, 82 | "trashed": false, 83 | "creator": { 84 | "id": 149087659, 85 | "name": "Jason Fried", 86 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 87 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 88 | }, 89 | "topic_url": "https://basecamp.com/9999999/api/v1/messages/888888.json" 90 | } 91 | ``` 92 | 93 | ### Attaching files 94 | 95 | Attaching files to a comment requires both the token and the name of the attachment. The 96 | token is returned from the [Create attachments](https://github.com/basecamp/bcx-api/blob/master/sections/attachments.md) 97 | endpoint, which you must hit first before creating an upload. 98 | 99 | The `name` parameter *must* be a valid filename with an extension. Multiple 100 | attachments are allowed. 101 | 102 | ```json 103 | { 104 | "content": "Here's the stuff", 105 | "attachments": [ 106 | { 107 | "token": "4f71ea23-134660425d1818169ecfdbaa43cfc07f4e33ef4c", 108 | "name": "final_mockup.png" 109 | }, 110 | { 111 | "token": "4f71ea23-458294fc0d87927301c5d54b69a7517602939e2c", 112 | "name": "draft_agreement.png" 113 | } 114 | ] 115 | } 116 | ``` 117 | 118 | 119 | Delete comment 120 | -------------- 121 | 122 | * `DELETE /projects/1/comments/1.json` will delete the comment specified and return `204 No Content` if that was successful. If the user does not have access to delete the comment, you'll see `403 Forbidden`. 123 | 124 | 125 | Private comments 126 | ---------------- 127 | 128 | Comments inherit the privacy of their commentables. For example, a comment on a private message is private. If a commentable is made public or private, so are all of its comments. 129 | 130 | Attachments on a comment inherit the privacy of its commentable. If a commentable is made public or private, so are all attachments on all of its comments. 131 | -------------------------------------------------------------------------------- /sections/documents.md: -------------------------------------------------------------------------------- 1 | Documents 2 | ========= 3 | 4 | All documents are automatically version-tracked. The API only exposes the most recent version of a document, though. Also, in the web UI we provide lock tracking to make sure people don't overwrite each other's work. There's no such automatic protection via the API. You're responsible yourself for managing this. Of course, everything is versioned so there won't be any lost data. 5 | 6 | Get documents 7 | ------------- 8 | 9 | * `GET /projects/1/documents.json` shows documents in one project. 10 | * `GET /documents.json` shows documents in all projects. 11 | 12 | ```json 13 | [ 14 | { 15 | "id": 963979453, 16 | "title": "Manifesto", 17 | "created_at": "2012-03-27T13:19:29-05:00", 18 | "updated_at": "2012-03-27T13:39:33-05:00", 19 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/documents/963979453.json", 20 | "app_url": "https://basecamp.com/999999999/projects/605816632/documents/963979453", 21 | "private": false, 22 | "trashed": false, 23 | "bucket": { 24 | "type": "Project", 25 | "id": 605816632, 26 | "name": "BCX", 27 | "color": "3185c5", 28 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 29 | "app_url": "https://basecamp.com/999999999/projects/605816632" 30 | } 31 | }, 32 | { 33 | "id": 243535881, 34 | "title": "Really important notes", 35 | "created_at": "2012-03-27T13:19:19-05:00", 36 | "updated_at": "2012-03-27T13:39:12-05:00", 37 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/documents/243535881.json", 38 | "app_url": "https://basecamp.com/999999999/projects/605816632/documents/243535881", 39 | "private": false, 40 | "trashed": false, 41 | "bucket": { 42 | "type": "Project", 43 | "id": 605816632, 44 | "name": "BCX", 45 | "color": "3185c5", 46 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 47 | "app_url": "https://basecamp.com/999999999/projects/605816632" 48 | } 49 | } 50 | ] 51 | ``` 52 | 53 | Buckets are only provided for documents that are not accessed via a project. 54 | For example, documents returned from `GET /documents.json` will include their 55 | buckets, but those returned from `GET /projects/1/documents.json` will not. 56 | 57 | ### Sorting 58 | 59 | It's possible to change the order documents are returned in with the `sort` 60 | parameter. Documents can be sorted by title or latest update time using the 61 | parameter values: 62 | 63 | * `az` and `za` for title 64 | * `newest` and `oldest` for latest update time 65 | 66 | The default sort is `newest`. 67 | 68 | 69 | Get document 70 | ------------ 71 | 72 | * `GET /projects/1/documents/1.json` will return the specified document along with all comments. 73 | 74 | ```json 75 | { 76 | "id": 963979453, 77 | "title": "Manifesto", 78 | "content": "Do this
Then that
Finally just so!", 79 | "created_at": "2012-03-27T13:19:29-05:00", 80 | "updated_at": "2012-03-27T13:53:24-05:00", 81 | "private": false, 82 | "trashed": false, 83 | "last_updater": { 84 | "id": 149087659, 85 | "name": "Jason Fried" 86 | }, 87 | "creator": { 88 | "id": 149087659, 89 | "name": "Jason Fried", 90 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 91 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 92 | }, 93 | "comments": [ 94 | { 95 | "content": "I think there should be more sass to it.", 96 | "created_at": "2012-03-27T13:53:24-05:00", 97 | "updated_at": "2012-03-27T13:53:24-05:00", 98 | "creator": { 99 | "id": 149087659, 100 | "name": "Jason Fried", 101 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 102 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 103 | } 104 | } 105 | ], 106 | "subscribers": [ 107 | { 108 | "id": 149087659, 109 | "name": "Jason Fried" 110 | }, 111 | { 112 | "id": 1071630348, 113 | "name": "Jeremy Kemper" 114 | } 115 | ] 116 | } 117 | ``` 118 | 119 | 120 | Create document 121 | --------------- 122 | 123 | * `POST /projects/1/documents.json` will create a new document from the parameters passed. 124 | 125 | ```json 126 | { 127 | "title": "Very important business notes", 128 | "content": "The TPS report is due on Monday morning!" 129 | } 130 | ``` 131 | 132 | This will return `201 Created`, with the location of the new project in the `Location` header along with a JSON representation of the document in the response body, if the creation was a success. See the **Get document** endpoint for more info. 133 | 134 | 135 | Update document 136 | --------------- 137 | 138 | * `PUT /projects/1/documents/1.json` will update the message from the parameters passed. 139 | 140 | ```json 141 | { 142 | "title": "Really, super duper important business notes", 143 | "content": "Now I want the report by SUNDAY!" 144 | } 145 | ``` 146 | 147 | This will return `200 OK` if the update was a success, along with a JSON representation of the document in the response body. See the **Get document** endpoint for more info. 148 | 149 | 150 | Delete document 151 | -------------- 152 | 153 | * `DELETE /projects/1/documents/1.json` will delete the document specified and return `204 No Content` if that was successful. If the user does not have access to delete the document, you'll see `403 Forbidden`. 154 | 155 | 156 | Private documents 157 | ----------------- 158 | 159 | To hide a document from clients, set its `private` attribute to `true`. 160 | 161 | ```json 162 | { 163 | "title": "Very important business notes", 164 | "content": "The TPS report is due on Monday morning!", 165 | "private": true 166 | } 167 | ``` 168 | 169 | To reveal a document to clients, set its `private` attribute to `false`. 170 | 171 | Comments on a document inherit its privacy. If a document is made public or private, so are all of its comments. 172 | -------------------------------------------------------------------------------- /sections/events.md: -------------------------------------------------------------------------------- 1 | Events 2 | ====== 3 | 4 | All actions in Basecamp generate an event for the progress log. If you start a new to-do list, there's an event. If you give someone access to a project, there's an event. If you add a comment. You get the drill. 5 | 6 | If you're using this API for polling, please make sure that you're using the `since` parameter to limit the result set. Use the `created_at` time of the first item on the list for subsequent polls. If there's nothing new since that date, you'll get `[]` back. 7 | 8 | 9 | Get events 10 | ----------------- 11 | 12 | * `GET /projects/1/events.json?since=2012-03-24T11:00:00-06:00` will return all events in the specified project since 11am CST on March 24, 2012. 13 | * `GET /people/1/events.json?since=2012-03-24T11:00:00-06:00` will return all the events created by the specified person since 11am CST on March 24, 2012. 14 | * `GET /events.json?since=2012-03-24T11:00:00-06:00` will return all events in all projects and calendars since 11am CST on March 24, 2012. 15 | 16 | Note that the `+` character must be url-escaped, while the `-` character can be used as-is. So, use `?since=2014-01-01T01:00:00%2B01:00` as opposed to `?since=2014-01-01T01:00:00+01:00` for east-of-GMT time zones. 17 | 18 | ```json 19 | [ 20 | { 21 | "id": 1054456336, 22 | "created_at": "2012-03-24T11:00:50-05:00", 23 | "updated_at": "2012-03-24T11:00:50-05:00", 24 | "private": false, 25 | "action": "re-assigned a to-do to Funky ones.:", 26 | "target": "Design it", 27 | "eventable": { 28 | "id": 223304243, 29 | "type": "Todo", 30 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/223304243.json", 31 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/223304243" 32 | }, 33 | "creator": { 34 | "id": 149087659, 35 | "name": "Jason Fried", 36 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 37 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 38 | }, 39 | "summary": "re-assigned a to-do to Funky ones: Design it", 40 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/223304243.json", 41 | "html_url": "https://basecamp.com/999999999/projects/605816632/todos/223304243" 42 | }, 43 | { 44 | "id": 1054456334, 45 | "created_at": "2012-03-24T11:00:39-05:00", 46 | "updated_at": "2012-03-24T11:00:39-05:00", 47 | "private": false, 48 | "action": "created a to-do list:", 49 | "target": "Launch list", 50 | "eventable": { 51 | "id": 1056802576, 52 | "type": "Todolist", 53 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/1056802576.json", 54 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/1056802576" 55 | }, 56 | "creator": { 57 | "id": 149087659, 58 | "name": "Jason Fried", 59 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 60 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 61 | }, 62 | "summary": "created a to-do list: Launch list", 63 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/1056802576.json", 64 | "html_url": "https://basecamp.com/999999999/projects/605816632/todolists/1056802576" 65 | }, 66 | { 67 | "id": 973672263, 68 | "created_at": "2012-03-24T09:53:35-05:00", 69 | "updated_at": "2012-03-24T09:53:35-05:00", 70 | "private": false, 71 | "action": "commented on", 72 | "target": "Prep the materials before the board meeting with Bezos", 73 | "eventable": { 74 | "id": 174886926, 75 | "type": "CalendarEvent", 76 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/174886926.json", 77 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/174886926" 78 | }, 79 | "creator": { 80 | "id": 149087659, 81 | "name": "Jason Fried", 82 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 83 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 84 | }, 85 | "attachments": [], 86 | "excerpt": "I'll be there!", 87 | "raw_excerpt": "I'll be there!", 88 | "summary": "commented on Prep the materials before the board meeting with Bezos", 89 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/174886926.json", 90 | "html_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/174886926" 91 | } 92 | ] 93 | ``` 94 | 95 | An event's summary contains a text description. To get the latest structured data, fetch the eventable. 96 | 97 | If an event's `created_at` and `updated_at` fields differ, it means that the eventable was updated within the 15 minute correction window we allow for people to fix small mistakes. 98 | 99 | Buckets are only provided for events that are not accessed via a project. For example, events returned from `GET /events.json` will include their buckets, 100 | but those returned from `GET /projects/1/events.json` will not. 101 | 102 | Creators are only provided for events that are not accessed via a person. For example, events returned from `GET /events.json` will include their creators, 103 | but those returned from `GET /people/1/events.json` will not. 104 | 105 | ### Pagination 106 | 107 | We will return 50 events per page. If the result set has 50 entries, it's your 108 | responsibility to check the next page to see if there are any more events -- 109 | you do this by adding `&page=2` to the query, then `&page=3` and so on. 110 | -------------------------------------------------------------------------------- /sections/forwards.md: -------------------------------------------------------------------------------- 1 | Forwards 2 | ======== 3 | 4 | Get forwards 5 | ------------- 6 | 7 | * `GET /projects/1/forwards.json` shows forwards in one project. 8 | * `GET /forwards.json` shows forwards in all projects. 9 | 10 | ```json 11 | [ 12 | { 13 | "id": 1072010356, 14 | "subject": "Proposal", 15 | "from": null, 16 | "created_at": "2014-08-19T15:33:08.000-05:00", 17 | "updated_at": "2014-08-19T15:33:12.000-05:00", 18 | "private": false, 19 | "trashed": false, 20 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/forwards/1072010356.json", 21 | "app_url": "https://basecamp.com/999999999/projects/605816632/forwards/1072010356", 22 | "bucket": { 23 | "id": 605816632, 24 | "name": "BCX", 25 | "type": "Project", 26 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 27 | "app_url": "https://basecamp.com/999999999/projects/605816632" 28 | } 29 | }, 30 | { 31 | "id": 617311580, 32 | "subject": "Prices", 33 | "from": "Tom McSalesman (tom@example.com)", 34 | "created_at": "2014-08-19T15:33:08.000-05:00", 35 | "updated_at": "2014-08-19T15:33:15.000-05:00", 36 | "private": false, 37 | "trashed": false, 38 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/forwards/617311580.json", 39 | "app_url": "https://basecamp.com/999999999/projects/605816632/forwards/617311580", 40 | "bucket": { 41 | "id": 605816632, 42 | "name": "BCX", 43 | "type": "Project", 44 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 45 | "app_url": "https://basecamp.com/999999999/projects/605816632" 46 | } 47 | } 48 | ] 49 | ``` 50 | 51 | Buckets are only provided for forwards that are not accessed via a project. For example, forwards returned from `GET /forwards.json` will include their buckets, 52 | but those returned from `GET /projects/1/forwards.json` will not. 53 | 54 | ### Sorting 55 | 56 | It's possible to change the order forwards are returned in with the `sort` 57 | parameter. Forwards can be sorted by subject or age using the parameter values: 58 | 59 | * `az` and `za` for subject 60 | * `newest` and `oldest` for age 61 | 62 | The default sort is `newest`. 63 | 64 | 65 | Get forward 66 | ------------ 67 | 68 | * `GET /projects/1/forwards/1.json` will return the specified forward along with all comments. 69 | 70 | ```json 71 | { 72 | "id": 617311580, 73 | "subject": "Prices", 74 | "from": "Tom McSalesman (tom@example.com)", 75 | "created_at": "2014-08-19T15:33:08.000-05:00", 76 | "updated_at": "2014-08-19T15:33:15.000-05:00", 77 | "private": false, 78 | "trashed": false, 79 | "content": "These prices are even better!", 80 | "content_html": "

These prices are even better!

Check out Basecamp!

", 81 | "creator": { 82 | "id": 149087659, 83 | "name": "Jason Fried", 84 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 85 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 86 | }, 87 | "attachments": [ 88 | { 89 | "key": "40b8a84cb1a30dbe04457dc99e094b6299deea41", 90 | "name": "bearwave.gif", 91 | "byte_size": 508254, 92 | "content_type": "image/gif", 93 | "created_at": "2012-03-27T22:48:49-04:00", 94 | "url": "https://basecamp.com/999999999/api/v1/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 95 | "creator": { 96 | "id": 149087659, 97 | "name": "Jason Fried", 98 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 99 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 100 | }, 101 | } 102 | ], 103 | "comments": [ 104 | { 105 | "id": 5566323, 106 | "content": "Testing a comment", 107 | "created_at": "2012-03-28T11:36:10-04:00", 108 | "updated_at": "2012-03-28T11:36:10-04:00", 109 | "attachments": [], 110 | "creator": { 111 | "id": 149087659, 112 | "name": "Jason Fried", 113 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 114 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 115 | } 116 | } 117 | ], 118 | "subscribers": [ 119 | { 120 | "id": 127326141, 121 | "name": "David Heinemeier Hansson" 122 | } 123 | ], 124 | "bucket": { 125 | "id": 605816632, 126 | "name": "BCX", 127 | "type": "Project", 128 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json" 129 | } 130 | } 131 | ``` 132 | -------------------------------------------------------------------------------- /sections/groups.md: -------------------------------------------------------------------------------- 1 | Companies / Groups 2 | ================== 3 | 4 | Use Companies and Groups to make sure everyone is in the right place, and to make invitations and notifications a snap! 5 | 6 | Inside the API companies are referred to as "groups, and departments as "subgroups". 7 | 8 | Get groups 9 | ---------- 10 | 11 | * `GET /groups.json` will return all groups and subgroups on the account. Only the groups a user has access to will be visible via the API. 12 | 13 | ```json 14 | [ 15 | { 16 | "created_at": "2014-09-24T09:12:00.000-05:00", 17 | "id": 261162085, 18 | "name": "Partners", 19 | "updated_at": "2014-09-26T16:13:55.000-05:00" 20 | }, 21 | { 22 | "id": 654632876, 23 | "name": "Basecamp", 24 | "created_at": "2014-09-24T09:12:00.000-05:00", 25 | "updated_at": "2014-09-29T10:05:22.000-05:00", 26 | "subgroups": [ 27 | { 28 | "created_at": "2014-09-25T16:04:39.000-05:00", 29 | "id": 1009501287, 30 | "name": "Designers", 31 | "updated_at": "2014-09-25T16:04:39.000-05:00" 32 | }, 33 | { 34 | "created_at": "2014-09-25T16:04:39.000-05:00", 35 | "id": 1009501288, 36 | "name": "Support", 37 | "updated_at": "2014-09-25T16:04:39.000-05:00" 38 | }, 39 | { 40 | "created_at": "2014-09-25T16:04:39.000-05:00", 41 | "id": 1009501289, 42 | "name": "Programmers", 43 | "updated_at": "2014-09-25T16:04:39.000-05:00" 44 | } 45 | ] 46 | } 47 | ] 48 | ``` 49 | 50 | Get group 51 | --------- 52 | 53 | * `GET /groups/1.json` will return the specified group and members of that group. Members of this groups subgroups will not be returned. See the **Get subgroup** endpoint to view an individual subgroup and it's members. If you request a group you do not have access to, you'll se a `404 Not Found`. 54 | 55 | ```json 56 | { 57 | "created_at": "2014-09-24T09:12:00.000-05:00", 58 | "id": 654632876, 59 | "name": "Basecamp", 60 | "subgroups": [ 61 | { 62 | "created_at": "2014-09-25T16:04:39.000-05:00", 63 | "id": 1009501287, 64 | "name": "Designers", 65 | "updated_at": "2014-09-25T16:04:39.000-05:00" 66 | }, 67 | { 68 | "created_at": "2014-09-25T16:04:39.000-05:00", 69 | "id": 1009501288, 70 | "name": "Support", 71 | "updated_at": "2014-09-25T16:04:39.000-05:00" 72 | }, 73 | { 74 | "created_at": "2014-09-25T16:04:39.000-05:00", 75 | "id": 1009501289, 76 | "name": "Programmers", 77 | "updated_at": "2014-09-25T16:04:39.000-05:00" 78 | } 79 | ], 80 | "memberships": [ 81 | { 82 | "id": 149087659, 83 | "identity_id": 982871737, 84 | "name": "Jason Fried", 85 | "email_address": "jason@basecamp.com", 86 | "admin": true, 87 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif", 88 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 89 | "created_at": "2012-03-22T16:56:48-05:00", 90 | "updated_at": "2012-03-22T16:56:48-05:00", 91 | "url": "https://basecamp.com/999999999/api/v1/people/149087659-jason-fried.json", 92 | "app_url": "https://basecamp.com/999999999/people/149087659-jason-fried" 93 | }, 94 | { 95 | "id": 1071630348, 96 | "identity_id": 827377171, 97 | "name": "Jeremy Kemper", 98 | "email_address": "jeremy@basecamp.com", 99 | "admin": true, 100 | "avatar_url": "https://asset0.37img.com/global/e68cafa694e8f22203eb36f13dccfefa9ac0acb2/avatar.96.gif", 101 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 102 | "created_at": "2012-03-22T16:56:48-05:00", 103 | "updated_at": "2012-03-22T16:56:48-05:00", 104 | "url": "https://basecamp.com/999999999/api/v1/people/1071630348-jeremy-kemper.json", 105 | "app_url": "https://basecamp.com/999999999/people/1071630348-jeremy-kemper" 106 | } 107 | ] 108 | } 109 | ``` 110 | 111 | Get subgroup 112 | ------------ 113 | 114 | * `GET /groups/1/subgroups/2.json` will return the specified sugroup and members of that subgroup. If you request a group you do not have access to, you'll se a `404 Not Found`. 115 | 116 | ```json 117 | { 118 | "created_at": "2014-09-25T16:04:39.000-05:00", 119 | "id": 1009501289, 120 | "name": "Programmers", 121 | "parent_id": 654632876, 122 | "updated_at": "2014-09-25T16:04:39.000-05:00", 123 | "memberships": [ 124 | { 125 | "id": 1071630348, 126 | "identity_id": 827377171, 127 | "name": "Jeremy Kemper", 128 | "email_address": "jeremy@basecamp.com", 129 | "admin": true, 130 | "avatar_url": "https://asset0.37img.com/global/e68cafa694e8f22203eb36f13dccfefa9ac0acb2/avatar.96.gif", 131 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 132 | "created_at": "2012-03-22T16:56:48-05:00", 133 | "updated_at": "2012-03-22T16:56:48-05:00", 134 | "url": "https://basecamp.com/999999999/api/v1/people/1071630348-jeremy-kemper.json", 135 | "app_url": "https://basecamp.com/999999999/people/1071630348-jeremy-kemper" 136 | } 137 | ] 138 | } 139 | ``` 140 | 141 | Create group 142 | ------------ 143 | 144 | * `POST /groups.json` will create a new group from the parameters passed. If no memeberships are passed the group will be invisible in the user interface of the application, but still accessible via the API if you are an admin. Only existing users can be added to companies or groups. Make sure you've added someone to your account before adding them to a group. 145 | 146 | ```json 147 | { 148 | "name": "New Company Name", 149 | "memberships": [ 150 | { 151 | "person_id": 1071630348 152 | } 153 | ] 154 | } 155 | ``` 156 | This will return `201 Created`, with the location of the new group in the `Location` header along with the current JSON representation of the group if the creation was a success. See the **Get group** enpoint for more information. If the user does not have access to the create new groups you'll see a `403 Forbidden`. 157 | 158 | Create subgroup 159 | --------------- 160 | 161 | * `POST /groups/1/subgroups.json` will create a new subgroup from the parameters passed. If no memberships are passed the subgroup will be invisible in the user interface of the application, but still accessible via the API. Only existing users can be added to subgroups. Make sure you've added someone to your account before adding them to a subgroup. 162 | 163 | 164 | ```json 165 | { 166 | "name": "Subgroup of Basecamp Group", 167 | "memberships": [ 168 | { 169 | "person_id": 1071630348 170 | } 171 | ] 172 | } 173 | ``` 174 | 175 | This will return `201 Created`, with the location of the new subgroup in the `Location` header along with the current JSON representation of the subgroup if the creation was a success. See the **Get subgroup** enpoint for more information. If the user does not have access to the create new groups you'll see a `403 Forbidden`. 176 | 177 | Update group 178 | ------------ 179 | 180 | * `PUT /groups/1.json` will update the group from the parameters passed. 181 | 182 | ```json 183 | { 184 | "name": "Update the group name" 185 | } 186 | ``` 187 | 188 | This will return a `200 OK` if the update was a success along with the current JSON representation of the group. See **Get group** endpoint for more information. If the user does not have access to update the group, you'll see `403 Forbiddden`. 189 | 190 | Add members to a group 191 | ---------------------- 192 | 193 | * `PUT /groups/1.json` will add new members to an existing group from the membership parameters passed. To remove a member from an existing group see the **Delete membership** endpoint. 194 | 195 | ```json 196 | { 197 | "memberships": [ 198 | { "person_id": 1071630348 }, 199 | { "person_id": 149087659 } 200 | ] 201 | } 202 | ``` 203 | 204 | This will return a `200 OK` if the update was a success along with the current JSON representation of the group. See **Get group** endpoint for more information. If the user does ont have access to update the members of a group, you'll see `403 Forbiddden`. 205 | 206 | Update subgroup 207 | --------------- 208 | 209 | * `PUT /groups/1/subgroups/2.json` will update the subgroup from the parameters passed. 210 | 211 | ```json 212 | { 213 | "name": "Update the subgroup name" 214 | } 215 | ``` 216 | 217 | This will return a `200 OK` if the update was a success along with the current JSON representation of the subgroup. See **Get subgroup** endpoint for more information. If the user does not have access to update the subgroup, you'll see `403 Forbiddden`. 218 | 219 | Add members to a subgroup 220 | ---------------------- 221 | 222 | * `PUT /groups/1/subgroups/2.json` will add new members to an existing subgroup from the parameters passed. To remove a member from an existing subgroup see the **Delete membership** endpoint. Users can only be added to a subgroup if they already belong to the parent group. 223 | 224 | ```json 225 | { 226 | "memberships": [ 227 | { "person_id": 1071630348 }, 228 | { "person_id": 149087659 } 229 | ] 230 | } 231 | ``` 232 | 233 | This will return a `200 OK` if the update was a success along with the current JSON representation of the subgroup. See **Get subgroup** endpoint for more information. If the user does not have access to update the members of a subgroup, you'll see `403 Forbiddden`. 234 | 235 | Delete group 236 | ------------ 237 | 238 | * `DELETE /groups/1.json` will delete the group specified and return `204 No Content` if the delete was successful. If the user does not have access to delete the group, you'll see a `403 Forbidden`. 239 | 240 | Delete subgroup 241 | --------------- 242 | 243 | * `DELETE /groups/1/subgroups/2.json` will delete the subgroup specifieid and return a `204 No Content` if the delete was successful. If the user does not have access to delete the subgroup, you'll see a `403 Forbidden`. 244 | 245 | Delete a member of a group 246 | ---------------------------- 247 | 248 | * `DELETE /groups/1/memberships/1.json` will delete the membership specified and return `204 No Content` if the delete was successful. If the user does not have access to delete the member, you'll see a `403 Forbidden`. The ID in `memberships/1.json` is the `person_id` not the membership ID because `person_id` is accessible via the groups API. The ID of the person can be found in the membership list on the group. If a member is deleted from the group they will also be deleted from all subgroups that are children of the group. 249 | 250 | Delete a member of a subgroup 251 | ---------------------------- 252 | 253 | * `DELETE /groups/1/subgroups/2/memberships/1.json` will delete the membership specified and return `204 No Content` if the delete was successful. If the user does not have access to delete the member, you'll see a `403 Forbidden`. The ID in `memberships/1.json` is the `person_id` not the membership ID because `person_id` is accessible via the subgroups API. The ID of the person can be found in the membership list on the subgroup. 254 | -------------------------------------------------------------------------------- /sections/messages.md: -------------------------------------------------------------------------------- 1 | Messages 2 | ======== 3 | 4 | > What a lot we lost when we stopped writing letters. You can't reread a phone call. - Liz Carpenter 5 | 6 | 7 | Get messages 8 | ------------ 9 | 10 | Messages are listed alongside all the other [topics](https://github.com/basecamp/bcx-api/blob/master/sections/topics.md), so there is no individual index for them. 11 | 12 | 13 | Get message 14 | ----------- 15 | 16 | * `GET /projects/1/messages/1.json` will return the specified message. 17 | 18 | ```json 19 | { 20 | "id": 936075699, 21 | "subject": "Welcome!", 22 | "created_at": "2012-03-22T16:56:51-05:00", 23 | "updated_at": "2012-03-22T16:56:51-05:00", 24 | "content": "This is a new message", 25 | "private": false, 26 | "trashed": false, 27 | "creator": { 28 | "id": 149087659, 29 | "name": "Jason Fried", 30 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 31 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 32 | }, 33 | "comments": [ 34 | { 35 | "id": 1028592764, 36 | "content": "Yeah, really, welcome!", 37 | "created_at": "2012-03-22T16:56:48-05:00", 38 | "updated_at": "2012-03-22T16:56:48-05:00", 39 | "creator": { 40 | "id": 149087659, 41 | "name": "Jason Fried", 42 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 43 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 44 | } 45 | } 46 | ], 47 | "subscribers": [ 48 | { 49 | "id": 149087659, 50 | "name": "Jason Fried" 51 | }, 52 | { 53 | "id": 1071630348, 54 | "name": "Jeremy Kemper" 55 | } 56 | ] 57 | } 58 | ``` 59 | 60 | 61 | Create message 62 | -------------- 63 | 64 | * `POST /projects/1/messages.json` will create a new message from the parameters passed. The subscribers array is an optional list of people IDs that you want to notify about this comment (see [Get accesses](https://github.com/basecamp/bcx-api/blob/master/sections/accesses.md) on how to get the people IDs for a given project). 65 | 66 | ```json 67 | { 68 | "subject": "Hello everyone", 69 | "content": "This is going to be a GREAT Saturday!", 70 | "subscribers": [1, 5, 6] 71 | } 72 | ``` 73 | 74 | To "loop-in" outside email addresses for users without an account, include a new_subscriber_emails array with the parameters. 75 | 76 | ```json 77 | { 78 | "subject": "Hello everyone", 79 | "content": "This is going to be a GREAT Saturday!", 80 | "new_subscriber_emails": ["example@example.com"] 81 | } 82 | ``` 83 | 84 | This will return `201 Created`, with the location of the new project in the `Location` header along with the current JSON representation of the message if the creation was a success. See the [Get message](https://github.com/basecamp/bcx-api/blob/master/sections/messages.md#get-message) endpoint for more info. 85 | 86 | ### Attaching files 87 | 88 | Attaching files to a message requires both the token and the name of the attachment. The 89 | token is returned from the [Create attachments](https://github.com/basecamp/bcx-api/blob/master/sections/attachments.md) 90 | endpoint, which you must hit first before creating an upload. 91 | 92 | The `name` parameter *must* be a valid filename with an extension. Multiple 93 | attachments are allowed. 94 | 95 | ```json 96 | { 97 | "subject": "Totally done!", 98 | "content": "I finished the reports, check them out!", 99 | "attachments": [ 100 | { 101 | "token": "4f73595a-39a6fd18317b1eeffb9c4734e95a179aa4b1b7c8", 102 | "name": "cover_page.pdf" 103 | }, 104 | { 105 | "token": "4f73595f-78efbe63c77a4f5c752ce7d113d0361220f70b69", 106 | "name": "final_draft.pdf" 107 | } 108 | ] 109 | } 110 | ``` 111 | 112 | 113 | Update message 114 | -------------- 115 | 116 | * `PUT /projects/1/messages/1.json` will update the message from the parameters passed. 117 | 118 | ```json 119 | { 120 | "subject": "This is a new subject for the message!", 121 | "content": "And new content..." 122 | } 123 | ``` 124 | 125 | This will return `200 OK` if the update was a success, along with the current JSON representation of the message in the response body. If the user does not have access to update the message, you'll see `403 Forbidden`. See the **Get message** endpoint for more info. 126 | 127 | 128 | Delete message 129 | ------------- 130 | 131 | * `DELETE /projects/1/messages/1.json` will delete the message specified and return `204 No Content` if that was successful. If the user does not have access to delete the message, you'll see `403 Forbidden`. 132 | 133 | 134 | Private messages 135 | ---------------- 136 | 137 | To hide a message from clients, set its `private` attribute to `true`. 138 | 139 | ```json 140 | { 141 | "subject": "Hello everyone", 142 | "content": "This is going to be a GREAT Saturday!", 143 | "subscribers": [1, 5, 6], 144 | "private": true 145 | } 146 | ``` 147 | 148 | To reveal a message to clients, set its `private` attribute to `false`. 149 | 150 | Comments and attachments on a message inherit its privacy. If a message is made public or private, so are all of its comments and attachments. 151 | -------------------------------------------------------------------------------- /sections/people.md: -------------------------------------------------------------------------------- 1 | People 2 | ====== 3 | 4 | > The major problems of our work are not so much technological as sociological in nature - T. DeMarco & T. Lister 5 | 6 | 7 | Get people 8 | ---------- 9 | 10 | * `GET /people.json` will return all people on the account. 11 | * `GET /people/trashed.json` will return all people who have been deleted from the account. Only admins are able to access trashed people. 12 | 13 | ```json 14 | [ 15 | { 16 | "id": 149087659, 17 | "identity_id": 982871737, 18 | "name": "Jason Fried", 19 | "email_address": "jason@basecamp.com", 20 | "admin": true, 21 | "trashed": false, 22 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif", 23 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 24 | "created_at": "2012-03-22T16:56:48-05:00", 25 | "updated_at": "2012-03-22T16:56:48-05:00", 26 | "url": "https://basecamp.com/999999999/api/v1/people/149087659-jason-fried.json", 27 | "app_url": "https://basecamp.com/999999999/people/149087659-jason-fried" 28 | }, 29 | { 30 | "id": 1071630348, 31 | "identity_id": 827377171, 32 | "name": "Jeremy Kemper", 33 | "email_address": "jeremy@basecamp.com", 34 | "admin": true, 35 | "trashed": false, 36 | "avatar_url": "https://asset0.37img.com/global/e68cafa694e8f22203eb36f13dccfefa9ac0acb2/avatar.96.gif", 37 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 38 | "created_at": "2012-03-22T16:56:48-05:00", 39 | "updated_at": "2012-03-22T16:56:48-05:00", 40 | "url": "https://basecamp.com/999999999/api/v1/people/1071630348-jeremy-kemper.json", 41 | "app_url": "https://basecamp.com/999999999/people/1071630348-jeremy-kemper" 42 | } 43 | ] 44 | ``` 45 | 46 | Get person 47 | ---------- 48 | 49 | * `GET /people/1.json` will return the specified person. 50 | * `GET /people/me.json` will return the current person. 51 | 52 | ```json 53 | { 54 | "id": 149087659, 55 | "identity_id": 982871737, 56 | "name": "Jason Fried", 57 | "email_address": "jason@basecamp.com", 58 | "admin": true, 59 | "trashed": false, 60 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 61 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3", 62 | "created_at": "2012-03-22T16:56:51-05:00", 63 | "updated_at": "2012-03-23T13:55:43-05:00", 64 | "events": { 65 | "count": 19, 66 | "updated_at": "2012-03-23T13:55:43-05:00", 67 | "url": "https://basecamp.com/999999999/api/v1/people/149087659-jason-fried/events.json", 68 | "app_url": "https://basecamp.com/999999999/people/149087659-jason-fried/events" 69 | }, 70 | "assigned_todos": { 71 | "count": 80, 72 | "updated_at": "2013-06-26T16:22:05.000-04:00", 73 | "url": "https://basecamp.com/999999999/api/v1/people/149087659-jason-fried/assigned_todos.json", 74 | "app_url": "https://basecamp.com/999999999/people/149087659-jason-fried/assigned_todos" 75 | } 76 | } 77 | ``` 78 | 79 | Get projects a person has access to 80 | ----------------------------------- 81 | 82 | * `GET /people/1/projects.json` will return a list of all projects a person has access to including draft, template, archived, and deleted projects. Projects that the requesting user does not have access to will not appear in the project list. If the requesting user does not have the ablity to view the person `404 Not Found will be returned.` 83 | 84 | ```json 85 | [ 86 | { 87 | "id": 605816632, 88 | "name": "BCX", 89 | "description": "The Next Generation", 90 | "updated_at": "2012-03-23T13:55:43-05:00", 91 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 92 | "template": false, 93 | "archived": false, 94 | "starred": true, 95 | "trashed": false, 96 | "draft":false, 97 | "is_client_project": false, 98 | "color": "3185c5" 99 | }, 100 | { 101 | "id": 684146117, 102 | "name": "Nothing here!", 103 | "description": null, 104 | "updated_at": "2012-03-22T16:56:51-05:00", 105 | "url": "https://basecamp.com/999999999/api/v1/projects/684146117.json", 106 | "template": false, 107 | "archived": false, 108 | "starred": false, 109 | "trashed": false, 110 | "draft":false, 111 | "is_client_project": true, 112 | "color": "3185c5" 113 | } 114 | ] 115 | ``` 116 | 117 | Create person 118 | ------------- 119 | 120 | New people can be invited directly to projects via the [accesses API](https://github.com/basecamp/bcx-api/blob/master/sections/accesses.md). 121 | 122 | 123 | Delete person 124 | ------------ 125 | 126 | * `DELETE /people/1.json` will delete the person specified and return `204 No Content` if that was successful. If the user does not have access to delete the person, you'll see `403 Forbidden`. 127 | -------------------------------------------------------------------------------- /sections/project_templates.md: -------------------------------------------------------------------------------- 1 | Project Templates 2 | ================= 3 | 4 | Get project templates 5 | --------------------- 6 | 7 | * `GET /project_templates.json` will return all project templates. 8 | 9 | ```json 10 | [ 11 | { 12 | "id": 605816632, 13 | "name": "Client Project", 14 | "description": "Let's get started!", 15 | "updated_at": "2012-03-23T13:55:43-05:00", 16 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 17 | "app_url": "https://basecamp.com/999999999/projects/605816632", 18 | "template" :true, 19 | "archived": false, 20 | "starred": false, 21 | "trashed": false, 22 | "is_client_project": true, 23 | "color": "3185c5" 24 | }, 25 | { 26 | "id": 684146117, 27 | "name": "Other Template", 28 | "description": null, 29 | "updated_at": "2012-03-22T16:56:51-05:00", 30 | "url": "https://basecamp.com/999999999/api/v1/projects/684146117.json", 31 | "app_url": "https://basecamp.com/999999999/projects/684146117", 32 | "template": true, 33 | "archived": false, 34 | "starred": false, 35 | "trashed": false, 36 | "is_client_project": false, 37 | "color": "3185c5" 38 | } 39 | ] 40 | ``` 41 | 42 | Get project template 43 | -------------------- 44 | 45 | Getting a project template is the same as getting a project via the API. 46 | 47 | 48 | * `GET /projects/1.json` will return the specified project template. 49 | 50 | ```json 51 | { 52 | "id": 1, 53 | "name": "Client Project", 54 | "description": "Let's get started!", 55 | "template": true, 56 | "archived": false, 57 | "created_at": "2012-03-23T13:55:43-05:00", 58 | "updated_at": "2012-03-23T13:55:43-05:00", 59 | "starred": false, 60 | "trashed": false, 61 | "is_client_project": true, 62 | "color": "3185c5", 63 | "creator": { 64 | "id": 149087659, 65 | "name": "Jason Fried", 66 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 67 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 68 | }, 69 | "accesses": { 70 | "count": 5, 71 | "updated_at": "2012-03-23T13:55:43-05:00", 72 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/accesses.json", 73 | "app_url": "https://basecamp.com/999999999/projects/605816632/accesses" 74 | }, 75 | "attachments": { 76 | "count": 0, 77 | "updated_at": null, 78 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/attachments.json", 79 | "app_url": "https://basecamp.com/999999999/projects/605816632/attachments" 80 | }, 81 | "calendar_events": { 82 | "count": 3, 83 | "updated_at": "2012-03-22T17:35:50-05:00", 84 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events.json", 85 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events" 86 | }, 87 | "documents": { 88 | "count": 0, 89 | "updated_at": null, 90 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/documents.json", 91 | "app_url": "https://basecamp.com/999999999/projects/605816632/documents" 92 | }, 93 | "topics": { 94 | "count": 2, 95 | "updated_at": "2012-03-22T17:35:50-05:00", 96 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/topics.json", 97 | "app_url": "https://basecamp.com/999999999/projects/605816632/topics" 98 | }, 99 | "todolists": { 100 | "remaining_count": 4, 101 | "completed_count": 0, 102 | "updated_at": "2012-03-23T12:59:23-05:00", 103 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists.json", 104 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists" 105 | } 106 | } 107 | ``` 108 | 109 | Create project template 110 | ---------------------- 111 | 112 | * `POST /project_templates.json` will create a new project template from the parameters passed. 113 | 114 | ```json 115 | { 116 | "name": "This is my new project template!", 117 | "description": "It's a real nice template!" 118 | } 119 | ``` 120 | 121 | This will return `201 Created`, with the location of the new project template in the `Location` header along with the current JSON representation of the project template if the creation was a success. See the **Get project** endpoint for more info. If the user does not have access to create new project templates you'll see `403 Forbidden`. 122 | 123 | Update project template 124 | ----------------------- 125 | 126 | Updating a project template is the same as updating a project via the API. 127 | 128 | * `PUT /projects/1.json` will update the project template from the parameters passed. 129 | 130 | ```json 131 | { 132 | "name": "This is a new name for the project template!", 133 | "description": "And a new description for my template" 134 | } 135 | ``` 136 | 137 | This will return `200 OK` if the update was a success along with the current JSON representation of the project. See the **Get project** endpoint for more info. If the user does not have access to update the project template, you'll see `403 Forbidden`. 138 | 139 | Delete project template 140 | ---------------------- 141 | 142 | Deleting a project template is the same as deleting a project via the API. 143 | 144 | * `DELETE /projects/1.json` will delete the project template specified and return `204 No Content` if that was successful. If the user does not have access to delete the project template, you'll see `403 Forbidden`. 145 | -------------------------------------------------------------------------------- /sections/projects.md: -------------------------------------------------------------------------------- 1 | Projects 2 | ======== 3 | 4 | > Operations keeps the lights on, strategy provides a light at the end of the tunnel, 5 | > but project management is the train engine that moves the organization forward - Joy Gumz 6 | 7 | 8 | Get projects 9 | ------------ 10 | 11 | * `GET /projects.json` will return all active projects. 12 | * `GET /projects/drafts.json` will return all draft projects. 13 | * `GET /projects/archived.json` will return all archived projects. This request is [paginated](https://github.com/basecamp/bcx-api?tab=readme-ov-file#pagination). 14 | 15 | ```json 16 | [ 17 | { 18 | "id": 605816632, 19 | "name": "BCX", 20 | "description": "The Next Generation", 21 | "updated_at": "2012-03-23T13:55:43-05:00", 22 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 23 | "app_url": "https://basecamp.com/999999999/projects/605816632", 24 | "template": false, 25 | "archived": false, 26 | "starred": true, 27 | "trashed": false, 28 | "draft":false, 29 | "is_client_project": false, 30 | "color": "3185c5" 31 | }, 32 | { 33 | "id": 684146117, 34 | "name": "Nothing here!", 35 | "description": null, 36 | "updated_at": "2012-03-22T16:56:51-05:00", 37 | "url": "https://basecamp.com/999999999/api/v1/projects/684146117.json", 38 | "app_url": "https://basecamp.com/999999999/projects/684146117", 39 | "template": false, 40 | "archived": false, 41 | "starred": false, 42 | "trashed": false, 43 | "draft":false, 44 | "is_client_project": true, 45 | "color": "3185c5" 46 | } 47 | ] 48 | ``` 49 | 50 | 51 | Get project 52 | ----------- 53 | 54 | * `GET /projects/1.json` will return the specified project. 55 | 56 | ```json 57 | { 58 | "id": 1, 59 | "name": "BCX", 60 | "description": "The Next Generation", 61 | "archived": false, 62 | "created_at": "2012-03-22T16:56:51-05:00", 63 | "updated_at": "2012-03-23T13:55:43-05:00", 64 | "template": false, 65 | "starred": true, 66 | "trashed": false, 67 | "draft":false, 68 | "is_client_project": false, 69 | "color": "3185c5", 70 | "creator": { 71 | "id": 149087659, 72 | "name": "Jason Fried", 73 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 74 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 75 | }, 76 | "accesses": { 77 | "count": 5, 78 | "updated_at": "2012-03-23T13:55:43-05:00", 79 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/accesses.json", 80 | "app_url": "https://basecamp.com/999999999/projects/605816632/accesses" 81 | }, 82 | "attachments": { 83 | "count": 0, 84 | "updated_at": null, 85 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/attachments.json", 86 | "app_url": "https://basecamp.com/999999999/projects/605816632/attachments" 87 | }, 88 | "calendar_events": { 89 | "count": 3, 90 | "updated_at": "2012-03-22T17:35:50-05:00", 91 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events.json", 92 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events" 93 | }, 94 | "documents": { 95 | "count": 0, 96 | "updated_at": null, 97 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/documents.json", 98 | "app_url": "https://basecamp.com/999999999/projects/605816632/documents" 99 | }, 100 | "topics": { 101 | "count": 2, 102 | "updated_at": "2012-03-22T17:35:50-05:00", 103 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/topics.json", 104 | "app_url": "https://basecamp.com/999999999/projects/605816632/topics" 105 | }, 106 | "todolists": { 107 | "remaining_count": 4, 108 | "completed_count": 0, 109 | "updated_at": "2012-03-23T12:59:23-05:00", 110 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists.json", 111 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists" 112 | } 113 | } 114 | ``` 115 | 116 | 117 | Create project 118 | -------------- 119 | 120 | * `POST /projects.json` will create a new project from the parameters passed. 121 | 122 | ```json 123 | { 124 | "name": "This is my new project!", 125 | "description": "It's going to run real smooth" 126 | } 127 | ``` 128 | 129 | This will return `201 Created`, with the location of the new project in the `Location` header along with the current JSON representation of the project if the creation was a success. See the **Get project** endpoint for more info. If the user does not have access to create new projects you'll see `403 Forbidden`. If the account has reached the project limit you'll see a `507 Insufficient Storage`. 130 | 131 | Create project from template 132 | ---------------------------- 133 | 134 | * `POST /project_templates/1/projects.json` will create a project from the template specified with the parameters passed. If a name and description are not passed the name and description from the template will be used instead. Projects created from templates will automatically be saved as draft projects. Before you start your project, you'll be able to make updates and changes. When you're ready for your project to go live you will need to publish your project. Until the project is published only the person who created the draft will be able to access it. 135 | 136 | ```json 137 | { 138 | "name": "This is my new project!", 139 | "description": "I was created from a template!" 140 | } 141 | ``` 142 | 143 | This will return `201 Created`, with the location of the new project in the `Location` header along with the current JSON representation of the project if the creation was a success. See the **Get project** endpoint for more info. If the user does not have access to create new projects you'll see `403 Forbidden`. If the account has reached the project limit you'll see a `507 Insufficient Storage`. 144 | 145 | 146 | Publishing a project 147 | -------------------- 148 | 149 | * `POST /projects/1/publish.json` will publish/activate a project created from a template (draft project). This will automatically send invitations to those you invited and make your project live. 150 | 151 | This will return `200 OK` if the update was a success, along with the current JSON representation of the project. If the user does not have access to update the project, you'll see `403 Forbidden`. 152 | 153 | 154 | Update project 155 | --------------- 156 | 157 | * `PUT /projects/1.json` will update the project from the parameters passed. 158 | 159 | ```json 160 | { 161 | "name": "This is a new name for the project!", 162 | "description": "And a new description..." 163 | } 164 | ``` 165 | 166 | This will return `200 OK` if the update was a success along with the current JSON representation of the project. See the **Get project** endpoint for more info. If the user does not have access to update the project, you'll see `403 Forbidden`. 167 | 168 | 169 | Archiving/activating a project 170 | ------------------------------ 171 | 172 | * `PUT /projects/1.json` with the following JSON payload will archive a project (pass false to activate it again). 173 | 174 | ```json 175 | { 176 | "archived": true 177 | } 178 | ``` 179 | 180 | This will return `200 OK` if the update was a success, along with the current JSON representation of the project. If the user does not have access to update the project, you'll see `403 Forbidden`. 181 | 182 | 183 | Delete project 184 | ------------- 185 | 186 | * `DELETE /projects/1.json` will delete the project specified and return `204 No Content` if that was successful. If the user does not have access to delete the project, you'll see `403 Forbidden`. 187 | 188 | 189 | Client projects 190 | --------------- 191 | 192 | * When creating or updating a project, set its `is_client_project` attribute to `true` to make it a client project. 193 | 194 | ```json 195 | { 196 | "is_client_project": true 197 | } 198 | ``` 199 | 200 | Set a project's `is_client_project` attribute to `false` to prevent content from being newly marked as private. Doing this will not make existing private content visible to clients. 201 | 202 | Content in a non-client project cannot be made private. Attempting to make content private in a non-client project will result in an error and a `422 Unprocessable Entity` response status. 203 | -------------------------------------------------------------------------------- /sections/stars.md: -------------------------------------------------------------------------------- 1 | Stars 2 | ===== 3 | 4 | Starring a project has its own API because people who don’t have permission to change a project can still star it. 5 | 6 | Note: Stars are included in project JSON as the `starred` attribute. 7 | 8 | 9 | Star a project 10 | -------------- 11 | 12 | * `POST /projects/123/star.json` stars the project. 13 | 14 | No request body is required. Expect a `201 Created` response with an empty body. 15 | 16 | 17 | Unstar a project 18 | ---------------- 19 | 20 | * `DELETE /projects/123/star.json` removes the star from the project. 21 | 22 | Expect a `204 No Content` response. 23 | 24 | 25 | List all stars 26 | -------------- 27 | 28 | * `GET /stars.json` lists stars on active projects. 29 | 30 | Expect a `200 OK` response with the JSON collection of starred project IDs, creation timestamps, and star URLs. 31 | 32 | ```json 33 | [ 34 | { 35 | "project_id": 605816632, 36 | "created_at": "2012-03-23T13:55:43-05:00", 37 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/star.json", 38 | "app_url": "https://basecamp.com/999999999/projects/605816632/star" 39 | }, 40 | { 41 | "project_id": 684146117, 42 | "created_at": "2012-03-22T16:56:51-05:00", 43 | "url": "https://basecamp.com/999999999/api/v1/projects/684146117/star.json", 44 | "app_url": "https://basecamp.com/999999999/projects/684146117/star" 45 | } 46 | ] 47 | ``` 48 | -------------------------------------------------------------------------------- /sections/tags.md: -------------------------------------------------------------------------------- 1 | Tags 2 | ==== 3 | 4 | Tags are referred to as "file labels" in Basecamp. 5 | 6 | In the API, tags are referenced by name. Tag names may contain characters that are not safe for URLs. When using tag names in API URLs, you **must** be careful to properly encode them. 7 | Failing to do so may result in `404 Not Found` errors if you attempt to access a tag with an unsafe name. For example, to delete a tag named "logos/icons" from a project, you should issue a `DELETE` request to 8 | `/projects/1/tags/logos%2Ficons.json`. More information on URL encoding is available [on Wikipedia](http://en.wikipedia.org/wiki/Percent-encoding). 9 | 10 | Tag names must contain at least one non-whitespace character. Leading and trailing whitespace is stripped from all tags. 11 | 12 | 13 | Add a tag to an attachment 14 | -------------------------- 15 | 16 | * `POST /projects/1/attachments/1/tags.json` will add a tag to an attachment and return `204 No Content` if that was successful. 17 | 18 | ```json 19 | {"name": "logos"} 20 | ``` 21 | 22 | 23 | Get attachments with a tag 24 | -------------------------- 25 | 26 | * `GET /projects/1/tags/logos/attachments.json` will return all attachments with the named tag that are visible to the current user. 27 | 28 | * `GET /tags/logos/attachments.json` will return all attachments with the named tag in all projects and calendars that are visible to the current user. 29 | 30 | Tag names are case-insensitive, so that two tags named "Logos" and "logos" are treated as equivalent. The same attachments will be returned by `GET /tags/logos/attachments.json` and `GET /tags/Logos/attachments.json`. 31 | 32 | 33 | Remove a tag from an attachment 34 | ------------------------------- 35 | 36 | * `DELETE /projects/1/attachments/1/tags/logos.json` will remove the named tag from the attachment and return `204 No Content` if that was successful. 37 | 38 | 39 | Rename a tag 40 | ------------ 41 | 42 | * `PUT /projects/1/attachments/1/tags/logos.json` will update the named tag in the project and return `204 No Content` if that was successful. 43 | 44 | * `PUT /tags/logos.json` will update the named tag in all projects and calendars visible to the current user and return `204 No Content` if that was successful. 45 | 46 | ```json 47 | {"name": "logos and branding"} 48 | ``` 49 | 50 | 51 | Delete a tag 52 | ------------ 53 | 54 | * `DELETE /projects/1/tags/logos.json` will delete the named tag from the project, removing it from all attachments in the project, and return `204 No Content` if that was successful. 55 | 56 | * `DELETE /tags/logos.json` will delete the named tag from all projects and calendars visible to the current user, removing it from all attachments, and return `204 No Content` if that was successful. 57 | -------------------------------------------------------------------------------- /sections/todolists.md: -------------------------------------------------------------------------------- 1 | To-do lists 2 | ========== 3 | 4 | Get to-do lists 5 | ------------- 6 | 7 | * `GET /projects/1/todolists.json` shows active to-do lists for this project sorted by position. 8 | * `GET /projects/1/todolists/completed.json` shows completed to-do lists for this project. 9 | * `GET /projects/1/todolists/trashed.json` shows trashed to-do lists for this project. 10 | * `GET /todolists.json` shows active to-do lists for all projects. 11 | * `GET /todolists/completed.json` shows completed to-do lists for all projects. 12 | * `GET /todolists/trashed.json` shows trashed to-do lists for all projects. 13 | 14 | ```json 15 | [ 16 | { 17 | "id": 968316918, 18 | "name": "Launch list", 19 | "description": "What we need for launch", 20 | "created_at": "2012-03-22T16:46:52-05:00", 21 | "updated_at": "2012-03-22T16:56:52-05:00", 22 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/968316918.json", 23 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/968316918", 24 | "completed": false, 25 | "position": 1, 26 | "private": false, 27 | "trashed": false, 28 | "completed_count": 3, 29 | "remaining_count": 5, 30 | "creator": { 31 | "id": 127326141, 32 | "name": "David Heinemeier Hansson", 33 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 34 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 35 | }, 36 | "bucket": { 37 | "id": 605816632, 38 | "name": "BCX", 39 | "type": "Project", 40 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 41 | "app_url": "https://basecamp.com/999999999/projects/605816632" 42 | } 43 | }, 44 | { 45 | "id": 812358930, 46 | "name": "Version 2", 47 | "description": "What we will do next", 48 | "created_at": "2012-03-22T16:46:52-05:00", 49 | "updated_at": "2012-03-22T16:56:52-05:00", 50 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/812358930.json", 51 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/812358930", 52 | "completed": false, 53 | "position": 2, 54 | "private": false, 55 | "trashed": false, 56 | "completed_count": 0, 57 | "remaining_count": 4, 58 | "creator": { 59 | "id": 127326141, 60 | "name": "David Heinemeier Hansson", 61 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 62 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 63 | }, 64 | "bucket": { 65 | "id": 605816632, 66 | "name": "BCX", 67 | "type": "Project", 68 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 69 | "app_url": "https://basecamp.com/999999999/projects/605816632" 70 | } 71 | } 72 | ] 73 | ``` 74 | 75 | Buckets are only provided for to-do lists that are not accessed via a project. For example, to-do lists returned from `GET /todolists.json` will include their buckets, 76 | but those returned from `GET /projects/1/todolists.json` will not. 77 | 78 | 79 | Get to-do lists with assigned to-dos 80 | --------------------------------- 81 | 82 | * `GET /people/1/assigned_todos.json` will return all the to-do lists with to-dos assigned to the specified person. 83 | * `GET /people/1/assigned_todos.json?due_since=2014-07-10` will return all the to-do lists with to-dos assigned to the specified person due after the date specified. 84 | 85 | ```json 86 | [ 87 | { 88 | "id": 968316918, 89 | "name": "Launch list", 90 | "description": "What we need for launch!", 91 | "created_at": "2012-03-22T16:46:52-05:00", 92 | "updated_at": "2012-03-29T11:00:39-05:00", 93 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/968316918.json", 94 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/968316918", 95 | "completed": false, 96 | "position": 1, 97 | "private": false, 98 | "trashed": false, 99 | "completed_count": 3, 100 | "remaining_count": 5, 101 | "creator": { 102 | "id": 127326141, 103 | "name": "David Heinemeier Hansson", 104 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 105 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 106 | }, 107 | "assigned_todos": [ 108 | { 109 | "id": 223304243, 110 | "content": "Design it", 111 | "due_at": "2012-03-30", 112 | "comments_count": 0, 113 | "created_at": "2012-03-27T13:19:30-05:00", 114 | "updated_at": "2012-03-29T11:00:38-05:00", 115 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/223304243.json", 116 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/223304243", 117 | "position": 1 118 | } 119 | ], 120 | "bucket": { 121 | "id": 605816632, 122 | "name": "BCX", 123 | "type": "Project", 124 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 125 | "app_url": "https://basecamp.com/999999999/projects/605816632" 126 | } 127 | }, 128 | { 129 | "id": 812358930, 130 | "name": "Version 2", 131 | "description": "What we will do next", 132 | "created_at": "2012-03-22T16:46:52-05:00", 133 | "updated_at": "2012-03-29T10:50:33-05:00", 134 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todolists/812358930.json", 135 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/812358930", 136 | "completed": false, 137 | "position": 2, 138 | "private": false, 139 | "trashed": false, 140 | "completed_count": 0, 141 | "remaining_count": 4, 142 | "creator": { 143 | "id": 127326141, 144 | "name": "David Heinemeier Hansson", 145 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 146 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 147 | }, 148 | "assigned_todos": [ 149 | { 150 | "id": 270524416, 151 | "content": "Fix all the bugs", 152 | "due_at": null, 153 | "comments_count": 0, 154 | "created_at": "2012-03-27T13:19:30-05:00", 155 | "updated_at": "2012-03-29T10:50:33-05:00", 156 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/270524416.json", 157 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/270524416", 158 | "position": 1 159 | } 160 | ], 161 | "bucket": { 162 | "id": 605816632, 163 | "name": "BCX", 164 | "type": "Project", 165 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 166 | "app_url": "https://basecamp.com/999999999/projects/605816632" 167 | } 168 | } 169 | ] 170 | ``` 171 | 172 | 173 | Get to-do list 174 | ------------ 175 | 176 | * `GET /projects/1/todolists/1.json` will return the specified to-do list including the to-dos. 177 | * `GET /projects/1/todolists/1.json?exclude_todos=true` will return the specified to-do list excluding the to-dos. 178 | If your to-do lists have a 1000+ total to-dos we request you use the to-do list with the exclude_todos parameter and 179 | retrieve to-dos from the [to-do endpoints](https://github.com/basecamp/bcx-api/blob/master/sections/todos.md#get-todos). 180 | 181 | ```json 182 | { 183 | "id": 968316918, 184 | "name": "Launch list", 185 | "description": "What we need for launch!", 186 | "created_at": "2012-03-24T09:53:35-05:00", 187 | "updated_at": "2012-03-24T09:59:35-05:00", 188 | "completed": false, 189 | "position": 1, 190 | "private": false, 191 | "trashed": false, 192 | "completed_count": 3, 193 | "remaining_count": 5, 194 | "creator": { 195 | "id": 127326141, 196 | "name": "David Heinemeier Hansson", 197 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 198 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 199 | }, 200 | "todos": { 201 | "remaining": [ 202 | { 203 | "id": 223304243, 204 | "content": "Design it", 205 | "due_at": "2012-03-24", 206 | "comments_count": 0, 207 | "created_at": "2012-03-24T09:53:35-05:00", 208 | "updated_at": "2012-03-24T09:55:52-05:00", 209 | "assignee": { 210 | "id": 149087659, 211 | "type": "Person", 212 | "name": "Jason Fried" 213 | }, 214 | "position": 1, 215 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/223304243.json", 216 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/223304243" 217 | }, 218 | { 219 | "id": 411008527, 220 | "content": "Test it", 221 | "due_at": null, 222 | "comments_count": 0, 223 | "created_at": "2012-03-24T09:53:35-05:00", 224 | "updated_at": "2012-03-24T09:53:35-05:00", 225 | "assignee": {}, 226 | "position": 2, 227 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/411008527.json", 228 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/411008527" 229 | } 230 | ], 231 | "completed": [ 232 | { 233 | "id": 1046098401, 234 | "content": "Think of it", 235 | "due_at": null, 236 | "comments_count": 0, 237 | "created_at": "2012-03-24T09:59:33-05:00", 238 | "updated_at": "2012-03-24T09:59:35-05:00", 239 | "completed_at": "2012-03-24T09:59:35-05:00", 240 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/1046098401.json", 241 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/1046098401", 242 | "assignee": {}, 243 | "position": 3, 244 | "completer": { 245 | "id": 149087659, 246 | "name": "Jason Fried" 247 | } 248 | } 249 | ], 250 | "trashed": [ 251 | { 252 | "id": 1046098402, 253 | "content": "Ship it", 254 | "due_at": null, 255 | "comments_count": 0, 256 | "created_at": "2012-03-24T09:59:33-05:00", 257 | "updated_at": "2012-03-24T09:59:35-05:00", 258 | "completed_at": "2012-03-24T09:59:35-05:00", 259 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/todos/1046098402.json", 260 | "app_url": "https://basecamp.com/999999999/projects/605816632/todos/1046098402", 261 | "assignee": {}, 262 | "position": 4, 263 | "completer": { 264 | "id": 149087659, 265 | "name": "Jason Fried" 266 | } 267 | } 268 | ] 269 | }, 270 | "comments": [ 271 | { 272 | "id": 1028592764, 273 | "content": "+1", 274 | "created_at": "2012-03-24T09:53:34-05:00", 275 | "updated_at": "2012-03-24T09:53:34-05:00", 276 | "creator": { 277 | "id": 127326141, 278 | "name": "David Heinemeier Hansson", 279 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 280 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 281 | } 282 | } 283 | ], 284 | "subscribers": [ 285 | { 286 | "id": 149087659, 287 | "name": "Jason Fried" 288 | }, 289 | { 290 | "id": 1071630348, 291 | "name": "Jeremy Kemper" 292 | } 293 | ] 294 | } 295 | ``` 296 | 297 | 298 | Create to-do list 299 | --------------- 300 | 301 | * `POST /projects/1/todolists.json` will create a new to-do list from the parameters passed. 302 | 303 | ```json 304 | { 305 | "name": "My really important list of stuff to do", 306 | "description": "I'm serial guys, this stuff matters!" 307 | } 308 | ``` 309 | 310 | This will return `201 Created`, with the URL of the new to-do list in the `Location` header along with the current JSON representation of the to-do list if the creation was a success. See the **Get to-do list** endpoint for more info. 311 | 312 | 313 | Update to-do list 314 | --------------- 315 | 316 | * `PUT /projects/1/todolists/1.json` will update the to-do list from the parameters passed. 317 | 318 | ```json 319 | { 320 | "name": "Think of a new title?", 321 | "description": "And a new description!" 322 | } 323 | ``` 324 | 325 | This will return `200 OK` if the creation was a success along with the current JSON representation of the to-do list in the response body. See the **Get to-do list** endpoint for more info. If the user does not have access to update the to-do list, you'll see `403 Forbidden`. 326 | 327 | ### Reordering to-do lists 328 | 329 | Updating the `position` of a to-do list is also possible through this endpoint by passing an integer between `1` and `n`, where `n` is the number of to-do lists in this project. 330 | 331 | ```json 332 | { 333 | "position": 2 334 | } 335 | ``` 336 | 337 | *Note*: If the position is out of bounds, the to-do will be moved to the bottom. 338 | 339 | 340 | Delete to-do list 341 | -------------- 342 | 343 | * `DELETE /projects/1/todolists/1.json` will delete the to-do list specified and return `204 No Content` if that was successful. If the user does not have access to delete the to-do list, you'll see `403 Forbidden`. 344 | 345 | 346 | Private to-do lists 347 | ----------------- 348 | 349 | To hide a to-do list and its to-dos from clients, set its `private` attribute to `true`. 350 | 351 | ```json 352 | { 353 | "name": "My really important list of stuff to do", 354 | "description": "I'm serial guys, this stuff matters!", 355 | "private": true 356 | } 357 | ``` 358 | 359 | To reveal a to-do list and its to-dos to clients, set its `private` attribute to `false`. 360 | 361 | Comments on a to-do list inherit its privacy. If a to-do list is made public or private, so are all of its comments. 362 | -------------------------------------------------------------------------------- /sections/todos.md: -------------------------------------------------------------------------------- 1 | To-dos 2 | ===== 3 | 4 | Get to-dos 5 | --------- 6 | 7 | To get an index of all to-dos on a list, see [to-do lists](https://github.com/basecamp/bcx-api/blob/master/sections/todolists.md). 8 | 9 | Per Project: 10 | 11 | * `GET /projects/1/todos.json` shows a list of all to-dos for this project; completed and remaining. 12 | * `GET /projects/1/todos/completed.json` shows a list of all completed to-dos for this project. 13 | * `GET /projects/1/todos/remaining.json` shows a list of all remaining/active to-dos for this project. 14 | * `GET /projects/1/todos.json?due_since=2014-07-10` will return all the to-dos due after the date specified. 15 | 16 | Per To-do List: 17 | * `GET /projects/1/todolists/1/todos.json` shows a list of all to-dos for this to-do list; completed and remaining. 18 | * `GET /projects/1/todolists/1/todos/completed.json` shows a list of all completed to-dos for this to-do list. 19 | * `GET /projects/1/todolists/1/todos/remaining.json` shows a list of all remaining to-dos for this to-do list. 20 | * `GET /projects/1/todolists/1/todos/trashed.json` shows a list of all trashed to-dos for this to-do list. 21 | 22 | ```json 23 | [ 24 | { 25 | "id": 1, 26 | "todolist_id": 1000, 27 | "position": 1, 28 | "content": "Test it", 29 | "due_at": null, 30 | "due_on": null, 31 | "created_at": "2012-03-24T09:53:35-05:00", 32 | "updated_at": "2012-03-24T10:56:33-05:00", 33 | "completed_at": false, 34 | "comments_count": 1, 35 | "private": false, 36 | "trashed": false, 37 | "creator": { 38 | "id": 127326141, 39 | "name": "David Heinemeier Hansson", 40 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 41 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 42 | }, 43 | "assignee": { 44 | "id": 149087659, 45 | "type": "Person", 46 | "name": "Jason Fried" 47 | }, 48 | "completed": false, 49 | "url": "http://37s.bcx.dev/999999999/api/v1/projects/605816632/todos/1.json", 50 | "app_url": "http://37s.bcx.dev/999999999/projects/605816632/todos/1", 51 | "todolist": { 52 | "completed": false, 53 | "completed_count": 1, 54 | "created_at": "2012-03-24T09:53:35-05:00", 55 | "creator": { 56 | "id": 127326141, 57 | "name": "David Heinemeier Hansson", 58 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 59 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 60 | }, 61 | "description": "What we will do next", 62 | "id": 1000, 63 | "name": "Launch list", 64 | "position": 3, 65 | "private": false, 66 | "remaining_count": 2, 67 | "trashed": false, 68 | "created_at": "2012-03-24T09:53:35-05:00", 69 | "url": "http://37s.bcx.dev/735644780/api/v1/projects/605816632/todolists/1.json", 70 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/1" 71 | } 72 | }, 73 | { 74 | "id": 2, 75 | "todolist_id": 1000, 76 | "position": 2, 77 | "content": "Test it", 78 | "due_at": null, 79 | "due_on": null, 80 | "created_at": "2012-03-24T09:53:35-05:00", 81 | "updated_at": "2012-03-24T10:56:33-05:00", 82 | "completed_at": false, 83 | "comments_count": 0, 84 | "private": false, 85 | "trashed": false, 86 | "creator": { 87 | "id": 127326141, 88 | "name": "David Heinemeier Hansson", 89 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 90 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 91 | }, 92 | "assignee": { 93 | "id": 149087659, 94 | "type": "Person", 95 | "name": "Jason Fried" 96 | }, 97 | "completed": false, 98 | "url": "http://37s.bcx.dev/999999999/api/v1/projects/605816632/todos/2.json", 99 | "app_url": "http://37s.bcx.dev/999999999/projects/605816632/todos/2", 100 | "todolist": { 101 | "completed": false, 102 | "completed_count": 1, 103 | "created_at": "2012-03-24T09:53:35-05:00", 104 | "creator": { 105 | "id": 127326141, 106 | "name": "David Heinemeier Hansson", 107 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 108 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 109 | }, 110 | "description": "What we will do next", 111 | "id": 1000, 112 | "name": "Launch list", 113 | "position": 3, 114 | "private": false, 115 | "remaining_count": 2, 116 | "trashed": false, 117 | "created_at": "2012-03-24T09:53:35-05:00", 118 | "url": "http://37s.bcx.dev/735644780/api/v1/projects/605816632/todolists/1.json", 119 | "app_url": "https://basecamp.com/999999999/projects/605816632/todolists/1" 120 | } 121 | } 122 | ] 123 | ``` 124 | 125 | Get to-do 126 | -------- 127 | 128 | * `GET /projects/1/todos/1.json` will return the specified to-do. 129 | 130 | ```json 131 | { 132 | "id": 1, 133 | "todolist_id": 1000, 134 | "position": 1, 135 | "content": "Test it", 136 | "due_at": null, 137 | "due_on": null, 138 | "created_at": "2012-03-24T09:53:35-05:00", 139 | "updated_at": "2012-03-24T10:56:33-05:00", 140 | "completed_at": false, 141 | "comments_count": 1, 142 | "private": false, 143 | "trashed": false, 144 | "creator": { 145 | "id": 127326141, 146 | "name": "David Heinemeier Hansson", 147 | "avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/avatar.96.gif?r=3", 148 | "fullsize_avatar_url": "https://asset0.37img.com/global/9d2148cb8ed8e2e8ecbc625dd1cbe7691896c7d9/original.gif?r=3" 149 | }, 150 | "assignee": { 151 | "id": 149087659, 152 | "type": "Person", 153 | "name": "Jason Fried" 154 | }, 155 | "completed": false, 156 | "url": "http://37s.bcx.dev/999999999/api/v1/projects/605816632/todos/1.json", 157 | "app_url": "http://37s.bcx.dev/999999999/projects/605816632/todos/1", 158 | "attachments": [], 159 | "subscribers": [ 160 | { 161 | "id": 149087659, 162 | "name": "Jason Fried" 163 | }, 164 | { 165 | "id": 127326141, 166 | "name": "David Heinemeier Hansson" 167 | } 168 | ] 169 | } 170 | ``` 171 | 172 | Create to-do 173 | ----------- 174 | 175 | * `POST /projects/1/todolists/1/todos.json` will add a new to-do to the specified to-do list from the parameters passed. The `due_at` parameter should be in ISO 8601 format (like "2012-03-27T16:00:00-05:00"). The assignee parameters need a `type` field with the `Person` specified. The `id` is then the id of the person who was assigned. 176 | 177 | ```json 178 | { 179 | "content": "This is my new thing!", 180 | "due_at": "2012-03-27", 181 | "assignee": { 182 | "id": 149087659, 183 | "type": "Person" 184 | } 185 | } 186 | ``` 187 | 188 | This will return `201 Created`, with the URL of the new to-do in the `Location` header along with the current JSON representation of the to-do if the creation was a success. See the **Get to-do** endpoint for more info. If the assignee type is unrecognized or the `due_at` is in a wrong format, you'll see a `400 Bad Request`. 189 | 190 | ### Attaching files 191 | 192 | Attaching files to a to-do requires both the token and the name of the attachment. The 193 | token is returned from the [Create attachments](https://github.com/basecamp/bcx-api/blob/master/sections/attachments.md) 194 | endpoint, which you must hit first before creating an upload. 195 | 196 | The `name` parameter *must* be a valid filename with an extension. Multiple 197 | attachments are allowed. 198 | 199 | ```json 200 | { 201 | "content": "This is my new thing!", 202 | "due_at": "2012-03-27", 203 | "assignee": { 204 | "id": 149087659, 205 | "type": "Person" 206 | }, 207 | "attachments": [ 208 | { 209 | "token": "4f73595a-39a6fd18317b1eeffb9c4734e95a179aa4b1b7c8", 210 | "name": "cover_page.pdf" 211 | }, 212 | { 213 | "token": "4f73595f-78efbe63c77a4f5c752ce7d113d0361220f70b69", 214 | "name": "final_draft.pdf" 215 | } 216 | ] 217 | } 218 | ``` 219 | 220 | 221 | Update to-do 222 | ----------- 223 | 224 | * `PUT /projects/1/todos/1.json` will update the to-do from the parameters passed. The `completed` field can be set to either `true` or `false` to check or uncheck the to-do. 225 | 226 | ```json 227 | { 228 | "content": "New content thing!", 229 | "due_at": "2012-03-27", 230 | "assignee": { 231 | "id": 149087659, 232 | "type": "Person" 233 | }, 234 | "completed": true 235 | } 236 | ``` 237 | 238 | This will return `200 OK` if the update was a success along with the current JSON representation of the to-do in the response body. See the **Get to-do** endpoint for more info. If the assignee type is unrecognized or the `due_at` is in a wrong format, you'll see a `400 Bad Request`. 239 | 240 | Sending a payload with `assignee` set to `null` will un-assign the to-do, and setting `due_at` to `null` will remove the due date. 241 | 242 | ```json 243 | { 244 | "due_at": null, 245 | "assignee": null 246 | } 247 | ``` 248 | 249 | ### Reordering to-dos 250 | 251 | Updating the `position` of a to-do is also possible through this endpoint by passing an integer between `1` and `n`, where `n` is the number of to-dos in this list. 252 | 253 | ```json 254 | { 255 | "position": 2 256 | } 257 | ``` 258 | 259 | *Note*: If the position is out of bounds, the to-do will be moved to the bottom. 260 | 261 | 262 | Delete to-do 263 | ---------- 264 | 265 | * `DELETE /projects/1/todos/1.json` will delete the to-do specified and return `204 No Content` if that was successful. If the user does not have access to delete the to-do, you'll see `403 Forbidden`. 266 | 267 | 268 | Private to-dos 269 | ------------- 270 | 271 | To-dos inherit the privacy of their to-do lists. A to-do on a private to-do list is private. If a to-do list is made private or public, so are all of its to-dos. 272 | 273 | Comments on a to-do inherit the privacy of its to-do list. If a to-do list is made public or private, so are all comments on all of its to-dos. 274 | -------------------------------------------------------------------------------- /sections/topics.md: -------------------------------------------------------------------------------- 1 | Topics 2 | ======== 3 | 4 | Topics refer to anything in Basecamp that can have comments: 5 | 6 | * Messages 7 | * Documents 8 | * Forwards 9 | * Calendar events 10 | * Uploads 11 | * To-do lists 12 | * To-dos 13 | 14 | Topics will only appear for resources that have at least one comment, except for Messages, which always have topics. 15 | 16 | 17 | Get topics 18 | ---------- 19 | 20 | * `GET /projects/1/topics.json` returns active topics in the specified project. 21 | * `GET /projects/1/topics/archived.json` returns archived topics in the specified project. 22 | * `GET /topics.json` returns active topics in all projects. 23 | * `GET /topics/archived.json` returns archived topics in all projects. 24 | 25 | ```json 26 | [ 27 | { 28 | "id": 1028592764, 29 | "title": "Prep the materials before the board meeting with Bezos", 30 | "excerpt": "I'll be there!", 31 | "created_at": "2012-03-24T09:53:35-05:00", 32 | "updated_at": "2012-03-24T09:53:35-05:00", 33 | "attachments": 0, 34 | "private": false, 35 | "trashed": false, 36 | "last_updater": { 37 | "id": 149087659, 38 | "name": "Jason Fried" 39 | }, 40 | "topicable": { 41 | "id": 174886926, 42 | "type": "CalendarEvent", 43 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/calendar_events/174886926.json", 44 | "app_url": "https://basecamp.com/999999999/projects/605816632/calendar_events/174886926" 45 | }, 46 | "bucket": { 47 | "id": 605816632, 48 | "name": "BCX", 49 | "type": "Project", 50 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 51 | "app_url": "https://basecamp.com/999999999/projects/605816632" 52 | } 53 | }, 54 | { 55 | "id": 936075699, 56 | "title": "Welcome!", 57 | "excerpt": "Yeah, really, welcome!", 58 | "created_at": "2012-03-24T09:53:35-05:00", 59 | "updated_at": "2012-03-24T09:53:35-05:00", 60 | "attachments": 1, 61 | "private": false, 62 | "trashed": false, 63 | "last_updater": { 64 | "id": 149087659, 65 | "name": "Jason Fried" 66 | }, 67 | "topicable": { 68 | "id": 936075699, 69 | "type": "Message", 70 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632/messages/936075699.json", 71 | "app_url": "https://basecamp.com/999999999/projects/605816632/messages/936075699" 72 | }, 73 | "bucket": { 74 | "id": 605816632, 75 | "name": "BCX", 76 | "type": "Project", 77 | "url": "https://basecamp.com/999999999/api/v1/projects/605816632.json", 78 | "app_url": "https://basecamp.com/999999999/projects/605816632" 79 | } 80 | } 81 | ] 82 | ``` 83 | 84 | For a topic: 85 | 86 | * `title` is the original title of the topicable 87 | * `excerpt` is from the latest comment on the topicable 88 | * `last_updater` is the creator of the latest comment, or the creator of the 89 | topicable if it has no comments 90 | 91 | Buckets are only provided for topics that are not accessed via a project. For 92 | example, topics returned from `GET /topics.json` will include their buckets, 93 | but those returned from `GET /projects/1/topics.json` will not. 94 | 95 | ### Pagination 96 | 97 | We will return 50 topics per page. If the result set has 50 topics, it's your 98 | responsibility to check the next page to see if there are any more topics -- 99 | you do this by adding `&page=2` to the query, then `&page=3` and so on. 100 | 101 | ### Sorting 102 | 103 | It's possible to change the order topics are returned in with the `sort` 104 | parameter. Topics can be sorted by their latest update times using the 105 | parameter values `newest` and `oldest`. The default sort is `newest`. 106 | 107 | Sorting can be combined with pagination. To get the second page of topics 108 | sorted by oldest first, request `/topics.json?page=2&sort=oldest`. 109 | 110 | 111 | Archive/activate a topic 112 | ------------------------ 113 | 114 | * `PUT /projects/1/topics/1/archive.json` will archive the specified topic. 115 | * `PUT /projects/1/topics/1/activate.json` will activate (reopen) the specified topic. 116 | 117 | No request body is necessary. In response, expect: 118 | 119 | * `204 No Content` if archiving/activating is successful 120 | * `403 Forbidden` if the topic's project is not active, or the authenticated user does not have permission 121 | -------------------------------------------------------------------------------- /sections/uploads.md: -------------------------------------------------------------------------------- 1 | Uploads 2 | ======= 3 | 4 | Uploads show up as "Files" inside of Basecamp. 5 | 6 | 7 | Create uploads 8 | -------------- 9 | 10 | * `POST /projects/1/uploads.json` will create a new entry in the "Files" section on the given project, with the given attachment token. 11 | 12 | This endpoint will return a `201 Created` if successful, with the URL to the new upload in the `Location` header along with the current JSON representation of the upload. See the **Get upload** for more info. 13 | 14 | Attaching files requires both the token and the name of the attachment. The 15 | token is returned from the [Create attachments](https://github.com/basecamp/bcx-api/blob/master/sections/attachments.md) 16 | endpoint, which you must hit first before creating an upload. 17 | 18 | The `name` parameter *must* be a valid filename with an extension. Only one 19 | attachment is allowed. 20 | 21 | The subscribers array is an optional list of people IDs that you want to notify about this comment (see [Get accesses](https://github.com/basecamp/bcx-api/blob/master/sections/accesses.md) on how to get the people IDs for a given project). 22 | 23 | ```json 24 | { 25 | "content": "Here's the new logo!", 26 | "attachments": [ 27 | { 28 | "token": "4f71ea23-134660425d1818169ecfdbaa43cfc07f4e33ef4c", 29 | "name": "new_logo.png" 30 | } 31 | ], 32 | "subscribers": [1, 5, 6] 33 | } 34 | ``` 35 | 36 | *Note*: Uploads can only have one attachment, despite the json blob accepting plural `attachments`. This is for consistency across the other endpoints that accept attachments. Hit the endpoint multiple times if you need to create multiple uploads. 37 | 38 | 39 | Get upload 40 | ---------- 41 | 42 | * `GET /projects/1/uploads/2.json` will show the content, comments, and attachments for this upload. 43 | 44 | Each attachment blob includes the `url` parameter, which you can make a 45 | `GET` request (with authentication) in order to directly download the attachment. 46 | 47 | ```json 48 | { 49 | "id": 31709, 50 | "created_at": "2012-03-27T22:48:49-04:00", 51 | "updated_at": "2012-03-28T11:36:10-04:00", 52 | "content": "Hi there!", 53 | "trashed": false, 54 | "attachments": [ 55 | { 56 | "key": "40b8a84cb1a30dbe04457dc99e094b6299deea41", 57 | "name": "bearwave.gif", 58 | "byte_size": 508254, 59 | "content_type": "image/gif", 60 | "created_at": "2012-03-27T22:48:49-04:00", 61 | "url": "https://basecamp.com/1111/api/v1/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 62 | "app_url": "https://basecamp.com/1111/projects/2222/attachments/3333/40b8a84cb1a30dbe04457dc99e094b6299deea41/original/bearwave.gif", 63 | "creator": { 64 | "id": 73, 65 | "name": "Nick Quaranto", 66 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 67 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 68 | } 69 | } 70 | ], 71 | "comments": [ 72 | { 73 | "id": 5566323, 74 | "content": "Testing a comment", 75 | "created_at": "2012-03-28T11:36:10-04:00", 76 | "updated_at": "2012-03-28T11:36:10-04:00", 77 | "attachments": [], 78 | "creator": { 79 | "id": 73, 80 | "name": "Nick Quaranto", 81 | "avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/avatar.96.gif?r=3", 82 | "fullsize_avatar_url": "https://asset0.37img.com/global/4113d0a133a32931be8934e70b2ea21efeff72c1/original.gif?r=3" 83 | } 84 | } 85 | ], 86 | "subscribers": [ 87 | { 88 | "id": 149087659, 89 | "name": "Jason Fried" 90 | }, 91 | { 92 | "id": 1071630348, 93 | "name": "Jeremy Kemper" 94 | } 95 | ] 96 | } 97 | ``` 98 | 99 | Delete upload 100 | ------------- 101 | 102 | * `DELETE /projects/1/uploads/2.json` will trash the upload specified and return `204 No Content` if that was successful. If the user does not have access to delete the upload, you'll see `403 Forbidden`. If the upload was deleted accidently simply retrieve it from the trash with the "bring it back" link in the application. Uploads cannot be permanently deleted via the API. 103 | 104 | Private uploads 105 | --------------- 106 | 107 | To hide an upload from clients, set its `private` attribute to `true`. 108 | 109 | ```json 110 | { 111 | "content": "Here's the new logo!", 112 | "attachments": [ 113 | { 114 | "token": "4f71ea23-134660425d1818169ecfdbaa43cfc07f4e33ef4c", 115 | "name": "new_logo.png" 116 | } 117 | ], 118 | "subscribers": [1, 5, 6], 119 | "private": true 120 | } 121 | ``` 122 | 123 | To reveal an upload to clients, set its `private` attribute to `false`. 124 | 125 | Comments and attachments on an upload inherit its privacy. If an upload is made public or private, so are all of its comments and attachments. 126 | --------------------------------------------------------------------------------