diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index e8b60697c..000000000 --- a/docs/API.md +++ /dev/null @@ -1,1779 +0,0 @@ -# Fizzy API - -Fizzy has an API that allows you to integrate your application with it or to create -a bot to perform various actions for you. - -## Authentication - -There are two ways to authenticate with the Fizzy API: - -1. **Personal access tokens** - Long-lived tokens for scripts and integrations -2. **Magic link authentication** - Session-based authentication for native apps - -### Personal Access Tokens - -To use the API you'll need an access token. To get one, go to your profile, then, -in the API section, click on "Personal access tokens" and then click on -"Generate new access token". - -Give it a description and pick what kind of permission you want the access token to have: -- `Read`: allows reading data from your account -- `Read + Write`: allows reading and writing data to your account on your behalf - -Then click on "Generate access token". - -
- Access token generation guide with screenshots - - | Step | Description | Screenshot | - |:----:|-------------|:----------:| - | 1 | Go to your profile | Profile page with API section | - | 2 | In the API section click on "Personal access token" | Personal access tokens page | - | 3 | Click on "Generate a new access token" | Generate new access token dialog | - | 4 | Give it a description and assign it a permission | Access token created | -
- -> [!IMPORTANT] -> __An access token is like a password, keep it secret and do not share it with anyone.__ -> Any person or application that has your access token can perform actions on your behalf. - -To authenticate a request using your access token, include it in the `Authorization` header: - -```bash -curl -H "Authorization: Bearer put-your-access-token-here" -H "Accept: application/json" https://app.fizzy.do/my/identity -``` - -### Magic Link Authentication - -For native apps, you can authenticate users via magic links. This is a two-step process: - -#### 1. Request a magic link - -Send the user's email address to request a magic link be sent to them: - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '{"email_address": "user@example.com"}' \ - https://app.fizzy.do/session -``` - -__Response:__ - -``` -HTTP/1.1 201 Created -Set-Cookie: pending_authentication_token=...; HttpOnly; SameSite=Lax -``` - -```json -{ - "pending_authentication_token": "eyJfcmFpbHMi..." -} -``` - -The response includes a `pending_authentication_token` both in the JSON body and as a cookie. -Native apps should store this token and include it as a cookie when submitting the magic link code. - -__Error responses:__ - -| Status Code | Description | -|--------|-------------| -| `422 Unprocessable entity` | Invalid email address, if sign ups are enabled and the value isn't a valid email address | -| `429 Too Many Requests` | Rate limit exceeded | - -#### 2. Submit the magic link code - -Once the user receives the magic link email, they'll have a 6-character code. Submit it to complete authentication: - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -H "Cookie: pending_authentication_token=eyJfcmFpbHMi..." \ - -d '{"code": "ABC123"}' \ - https://app.fizzy.do/session/magic_link -``` - -__Response:__ - -```json -{ - "session_token": "eyJfcmFpbHMi..." -} -``` - -The `session_token` can be used to authenticate subsequent requests by including it as a cookie: - -```bash -curl -H "Cookie: session_token=eyJfcmFpbHMi..." \ - -H "Accept: application/json" \ - https://app.fizzy.do/my/identity -``` - -__Error responses:__ - -| Status Code | Description | -|--------|-------------| -| `401 Unauthorized` | Invalid `pending_authentication_token` or `code` | -| `429 Too Many Requests` | Rate limit exceeded | - - -#### Delete server-side session (_log out_) - -To log out and destroy the server-side session: - -```bash -curl -X DELETE \ - -H "Accept: application/json" \ - -H "Cookie: session_token=eyJfcmFpbHMi..." \ - https://app.fizzy.do/session -``` - -__Response:__ - -Returns `204 No Content` on success. - -#### Create an access token via the API - -You can programmatically create a personal access token using either a session cookie or an existing Bearer token: - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -H "Cookie: session_token=eyJfcmFpbHMi..." \ - -d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \ - https://app.fizzy.do/1234567/my/access_tokens -``` - -Or with a Bearer token (must have `write` permission): - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer put-your-access-token-here" \ - -d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \ - https://app.fizzy.do/1234567/my/access_tokens -``` - -The `permission` field accepts `read` or `write`. - -__Response:__ - -``` -HTTP/1.1 201 Created -``` - -```json -{ - "token": "4f9Q6d2wXr8Kp1Ls0Vz3BnTa", - "description": "Fizzy CLI", - "permission": "write" -} -``` - -Store the `token` value securely — it won't be retrievable again. Use it as a Bearer token for subsequent API requests. - -## Caching - -Most endpoints return [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag) and [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control) headers. You can use these to avoid re-downloading unchanged data. - -### Using ETags - -When you make a request, the response includes an `ETag` header: - -``` -HTTP/1.1 200 OK -ETag: "abc123" -Cache-Control: max-age=0, private, must-revalidate -``` - -On subsequent requests, include the ETag value in the `If-None-Match` header: - -``` -GET /1234567/cards/42.json -If-None-Match: "abc123" -``` - -If the resource hasn't changed, you'll receive a `304 Not Modified` response with no body, saving bandwidth and processing time: - -``` -HTTP/1.1 304 Not Modified -ETag: "abc123" -``` - -If the resource has changed, you'll receive the full response with a new ETag. - -__Example in Ruby:__ - -```ruby -# Store the ETag from the response -etag = response.headers["ETag"] - -# On next request, send it back -headers = { "If-None-Match" => etag } -response = client.get("/1234567/cards/42.json", headers: headers) - -if response.status == 304 - # Nothing to do, the card hasn't changed -else - # The card has changed, process the new data -end -``` - -## Error Responses - -When a request fails, the API response will communicate the source of the problem through the HTTP status code. - -| Status Code | Description | -|-------------|-------------| -| `400 Bad Request` | The request was malformed or missing required parameters | -| `401 Unauthorized` | Authentication failed or access token is invalid | -| `403 Forbidden` | You don't have permission to perform this action | -| `404 Not Found` | The requested resource doesn't exist or you don't have access to it | -| `422 Unprocessable Entity` | Validation failed (see error response format above) | -| `500 Internal Server Error` | An unexpected error occurred on the server | - -If a request contains invalid data for fields, such as entering a string into a number field, in most cases the API will respond with a `500 Internal Server Error`. Clients are expected to perform some validation on their end before making a request. - -A validation error will produce a `422 Unprocessable Entity` response, which will sometimes be accompanied by details about the error: - -```json -{ - "avatar": ["must be a JPEG, PNG, GIF, or WebP image"] -} -``` - -## Pagination - -All endpoints that return a list of items are paginated. The page size can vary from endpoint to endpoint, -and we use a dynamic page size where initial pages return fewer results than later pages. - -If there are more results to fetch, the response will include a `Link` header with a `rel="next"` link to the next page of results: - -```bash -curl -H "Authorization: Bearer put-your-access-token-here" -H "Accept: application/json" -v http://fizzy.localhost:3006/686465299/cards -# ... -< link: ; rel="next" -# ... -``` - -## List parameters - -When an endpoint accepts a list of values as a parameter, you can provide multiple values by repeating the parameter name: - -``` -?tag_ids[]=tag1&tag_ids[]=tag2&tag_ids[]=tag3 -``` - -List parameters always end with `[]`. - -## File Uploads - -Some endpoints accept file uploads. To upload a file, send a `multipart/form-data` request instead of JSON. -You can combine file uploads with other parameters in the same request. - -__Example using curl:__ - -```bash -curl -X PUT \ - -H "Authorization: Bearer put-your-access-token-here" \ - -F "user[name]=David H. Hansson" \ - -F "user[avatar]=@/path/to/avatar.jpg" \ - http://fizzy.localhost:3006/686465299/users/03f5v9zjw7pz8717a4no1h8a7 -``` - -## Rich Text Fields - -Some fields accept rich text content. These fields accept HTML input, which will be sanitized to remove unsafe tags and attributes. - -```json -{ - "card": { - "title": "My card", - "description": "

This is bold and this is italic.

" - } -} -``` - -### Attaching files to rich text - -To attach files (images, documents) to rich text fields, use ActionText's direct upload flow: - -#### 1. Create a direct upload - -First, request a direct upload URL by sending file metadata: - -```bash -curl -X POST \ - -H "Authorization: Bearer put-your-access-token-here" \ - -H "Content-Type: application/json" \ - -d '{ - "blob": { - "filename": "screenshot.png", - "byte_size": 12345, - "checksum": "GQ5SqLsM7ylnji0Wgd9wNA==", - "content_type": "image/png" - } - }' \ - https://app.fizzy.do/123456/rails/active_storage/direct_uploads -``` - -The `checksum` is a Base64-encoded MD5 hash of the file content. -The direct upload endpoint is scoped to your account (replace `/123456` with your account slug). - -__Response:__ - -```json -{ - "id": "abc123", - "key": "abc123def456", - "filename": "screenshot.png", - "content_type": "image/png", - "byte_size": 12345, - "checksum": "GQ5SqLsM7ylnji0Wgd9wNA==", - "direct_upload": { - "url": "https://storage.example.com/...", - "headers": { - "Content-Type": "image/png", - "Content-MD5": "GQ5SqLsM7ylnji0Wgd9wNA==" - } - }, - "signed_id": "eyJfcmFpbHMi..." -} -``` - -#### 2. Upload the file - -Upload the file directly to the provided URL with the specified headers: - -```bash -curl -X PUT \ - -H "Content-Type: image/png" \ - -H "Content-MD5: GQ5SqLsM7ylnji0Wgd9wNA==" \ - --data-binary @screenshot.png \ - "https://storage.example.com/..." -``` - -#### 3. Reference the file in rich text - -Use the `signed_id` from step 1 to embed the file in your rich text using an `` tag: - -```json -{ - "card": { - "title": "Card with image", - "description": "

Here's a screenshot:

" - } -} -``` - -The `sgid` attribute should contain the `signed_id` returned from the direct upload response. - -## Identity - -An Identity represents a person using Fizzy. - -### `GET /my/identity` - -Returns a list of accounts the identity has access to, including the user for each account. - -```json -{ - "accounts": [ - { - "id": "03f5v9zjskhcii2r45ih3u1rq", - "name": "37signals", - "slug": "/897362094", - "created_at": "2025-12-05T19:36:35.377Z", - "user": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/users/03f5v9zjw7pz8717a4no1h8a7" - } - }, - { - "id": "03f5v9zpko7mmhjzwum3youpp", - "name": "Honcho", - "slug": "/686465299", - "created_at": "2025-12-05T19:36:36.746Z", - "user": { - "id": "03f5v9zppzlksuj4mxba2nbzn", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:36.783Z", - "url": "http://fizzy.localhost:3006/users/03f5v9zppzlksuj4mxba2nbzn" - } - } - ] -} -``` - -## Boards - -Boards are where you organize your work - they contain your cards. - -### `GET /:account_slug/boards` - -Returns a list of boards that you can access in the specified account. - -__Response:__ - -```json -[ - { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - } - } -] -``` - -### `GET /:account_slug/boards/:board_id` - -Returns the specified board. - -__Response:__ - -```json -{ - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "public_url": "http://fizzy.localhost:3006/897362094/public/boards/aB3dEfGhIjKlMnOp" -} -``` - -The `public_url` field is only present when the board is published. - -### `POST /:account_slug/boards` - -Creates a new Board in the account. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `name` | string | Yes | The name of the board | -| `all_access` | boolean | No | Whether any user in the account can access this board. Defaults to `true` | -| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | -| `public_description` | string | No | Rich text description shown on the public board page | - -__Request:__ - -```json -{ - "board": { - "name": "My new board", - } -} -``` - -__Response:__ - -Returns `201 Created` with a `Location` header pointing to the new board: - -``` -HTTP/1.1 201 Created -Location: /897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm.json -``` - -### `PUT /:account_slug/boards/:board_id` - -Updates a Board. Only board administrators can update a board. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `name` | string | No | The name of the board | -| `all_access` | boolean | No | Whether any user in the account can access this board | -| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | -| `public_description` | string | No | Rich text description shown on the public board page | -| `user_ids` | array | No | Array of *all* user IDs who should have access to this board (only applicable when `all_access` is `false`) | - -__Request:__ - -```json -{ - "board": { - "name": "Updated board name", - "auto_postpone_period_in_days": 30, - "public_description": "This is a **public** description of the board.", - "all_access": false, - "user_ids": [ - "03f5v9zppzlksuj4mxba2nbzn", - "03f5v9zjw7pz8717a4no1h8a7" - ] - } -} -``` - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/boards/:board_id` - -Deletes a Board. Only board administrators can delete a board. - -__Response:__ - -Returns `204 No Content` on success. - -## Board Publications - -Publishing a board makes it publicly accessible via a shareable link, without requiring authentication. Only board administrators can publish or unpublish a board. - -### `POST /:account_slug/boards/:board_id/publication` - -Publishes a board, generating a shareable public link. - -__Response:__ - -``` -HTTP/1.1 201 Created -``` - -```json -{ - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "public_url": "http://fizzy.localhost:3006/897362094/public/boards/aB3dEfGhIjKlMnOp" -} -``` - -If the board is already published, the existing publication is returned. - -### `DELETE /:account_slug/boards/:board_id/publication` - -Unpublishes a board, removing public access. - -__Response:__ - -Returns `204 No Content` on success. - -## Account - -### `GET /account/settings` - -Returns the current account. - -__Response:__ - -```json -{ - "id": "03f5v9zjvypwh0t0e2rfh0h7k", - "name": "37signals", - "cards_count": 5, - "created_at": "2025-12-05T19:36:35.401Z", - "auto_postpone_period_in_days": 30 -} -``` - -The `auto_postpone_period_in_days` is the account-level default in days (e.g. `30`). Cards are automatically moved to "Not Now" after this period of inactivity. Each board can override this with its own value. - -### `PUT /account/entropy` - -Updates the account-level default auto close period. Requires admin role. - -__Request:__ - -```json -{ - "entropy": { - "auto_postpone_period_in_days": 30 - } -} -``` - -__Response:__ - -Returns the account object: - -```json -{ - "id": "03f5v9zjvypwh0t0e2rfh0h7k", - "name": "37signals", - "cards_count": 5, - "created_at": "2025-12-05T19:36:35.401Z", - "auto_postpone_period_in_days": 30 -} -``` - -### `PUT /:account_slug/boards/:board_id/entropy` - -Updates the auto close period for a specific board. Requires board admin permission. - -__Request:__ - -```json -{ - "board": { - "auto_postpone_period_in_days": 90 - } -} -``` - -__Response:__ - -Returns the board object. - -## Webhooks - -Webhooks notify another application when something happens on a board. Only account admins can list, view, create, update, delete, or reactivate webhooks. - -### `GET /:account_slug/boards/:board_id/webhooks` - -Returns a paginated list of webhooks for a board. - -__Response:__ - -```json -[ - { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Production API", - "payload_url": "https://api.example.com/webhooks", - "active": true, - "signing_secret": "p94Bx2HjempCdYB4DTyZkY1b", - "subscribed_actions": ["card_published", "card_assigned", "card_closed"], - "created_at": "2025-12-05T19:36:35.534Z", - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy/webhooks/03f5v9zkft4hj9qq0lsn9ohcm", - "board": { - "id": "03f5v9zkft4hj9qq0lsn9ohcy", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - } - } - } -] -``` - -### `GET /:account_slug/boards/:board_id/webhooks/:id` - -Returns a single webhook. - -__Response:__ - -Returns the same webhook shape shown above. - -### `POST /:account_slug/boards/:board_id/webhooks` - -Creates a webhook. - -__Request:__ - -```json -{ - "webhook": { - "name": "Production API", - "url": "https://api.example.com/webhooks", - "subscribed_actions": ["card_published", "card_assigned", "card_closed"] - } -} -``` - -`subscribed_actions` accepts any of: -`card_assigned`, `card_closed`, `card_postponed`, `card_auto_postponed`, `card_board_changed`, `card_published`, `card_reopened`, `card_sent_back_to_triage`, `card_triaged`, `card_unassigned`, `comment_created` - -__Response:__ - -``` -HTTP/1.1 201 Created -Location: http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy/webhooks/03f5v9zkft4hj9qq0lsn9ohcm.json -``` - -Returns the created webhook in the response body. - -### `PATCH /:account_slug/boards/:board_id/webhooks/:id` - -Updates a webhook. - -__Request:__ - -```json -{ - "webhook": { - "name": "Production API", - "subscribed_actions": ["card_closed"] - } -} -``` - -The `url` is immutable after creation and is ignored on update. - -__Response:__ - -Returns the updated webhook. - -### `DELETE /:account_slug/boards/:board_id/webhooks/:id` - -Deletes a webhook. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/boards/:board_id/webhooks/:id/activation` - -Reactivates a deactivated webhook. - -__Response:__ - -``` -HTTP/1.1 201 Created -``` - -Returns the reactivated webhook in the response body. - -## Cards - -Cards are tasks or items of work on a board. They can be organized into columns, tagged, assigned to users, and have comments. - -### `GET /:account_slug/cards` - -Returns a paginated list of cards you have access to. Results can be filtered using query parameters. - -__Query Parameters:__ - -| Parameter | Description | -|-----------|-------------| -| `board_ids[]` | Filter by board ID(s) | -| `tag_ids[]` | Filter by tag ID(s) | -| `assignee_ids[]` | Filter by assignee user ID(s) | -| `creator_ids[]` | Filter by card creator ID(s) | -| `closer_ids[]` | Filter by user ID(s) who closed the cards | -| `card_ids[]` | Filter to specific card ID(s) | -| `indexed_by` | Filter by: `all` (default), `closed`, `not_now`, `stalled`, `postponing_soon`, `golden` | -| `sorted_by` | Sort order: `latest` (default), `newest`, `oldest` | -| `assignment_status` | Filter by assignment status: `unassigned` | -| `creation` | Filter by creation date: `today`, `yesterday`, `thisweek`, `lastweek`, `thismonth`, `lastmonth`, `thisyear`, `lastyear` | -| `closure` | Filter by closure date: `today`, `yesterday`, `thisweek`, `lastweek`, `thismonth`, `lastmonth`, `thisyear`, `lastyear` | -| `terms[]` | Search terms to filter cards | - -__Response:__ - -```json -[ - { - "id": "03f5vaeq985jlvwv3arl4srq2", - "number": 1, - "title": "First!", - "status": "published", - "description": "Hello, World!", - "description_html": "

Hello, World!

", - "image_url": null, - "has_attachments": false, - "tags": ["programming"], - "golden": false, - "last_active_at": "2025-12-05T19:38:48.553Z", - "created_at": "2025-12-05T19:38:48.540Z", - "url": "http://fizzy.localhost:3006/897362094/cards/4", - "board": { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - } - }, - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments", - "reactions_url": "http://fizzy.localhost:3006/897362094/cards/4/reactions" - }, -] -``` - -### `GET /:account_slug/cards/:card_number` - -Returns a specific card by its number. - -__Response:__ - -```json -{ - "id": "03f5vaeq985jlvwv3arl4srq2", - "number": 1, - "title": "First!", - "status": "published", - "description": "Hello, World!", - "description_html": "

Hello, World!

", - "image_url": null, - "has_attachments": false, - "tags": ["programming"], - "closed": false, - "golden": false, - "last_active_at": "2025-12-05T19:38:48.553Z", - "created_at": "2025-12-05T19:38:48.540Z", - "url": "http://fizzy.localhost:3006/897362094/cards/4", - "board": { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - } - }, - "column": { - "id": "03f5v9zkft4hj9qq0lsn9ohcn", - "name": "In Progress", - "color": { - "name": "Lime", - "value": "var(--color-card-4)" - }, - "created_at": "2025-12-05T19:36:35.534Z" - }, - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments", - "reactions_url": "http://fizzy.localhost:3006/897362094/cards/4/reactions", - "steps": [ - { - "id": "03f8huu0sog76g3s975963b5e", - "content": "This is the first step", - "completed": false - }, - { - "id": "03f8huu0sog76g3s975969734", - "content": "This is the second step", - "completed": false - } - ] -} -``` - -> **Note:** The `closed` field indicates whether the card is in the "Done" state. The `column` field is only present when the card has been triaged into a column; cards in "Maybe?", "Not Now" or "Done" will not have this field. - -### `POST /:account_slug/boards/:board_id/cards` - -Creates a new card in a board. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `title` | string | Yes | The title of the card | -| `description` | string | No | Rich text description of the card | -| `status` | string | No | Initial status: `published` (default), `drafted` | -| `image` | file | No | Header image for the card | -| `tag_ids` | array | No | Array of tag IDs to apply to the card | -| `created_at` | datetime | No | Override creation timestamp (ISO 8601 format) | -| `last_active_at` | datetime | No | Override last activity timestamp (ISO 8601 format) | - -__Request:__ - -```json -{ - "card": { - "title": "Add dark mode support", - "description": "We need to add dark mode to the app" - } -} -``` - -__Response:__ - -Returns `201 Created` with a `Location` header pointing to the new card. - -### `PUT /:account_slug/cards/:card_number` - -Updates a card. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `title` | string | No | The title of the card | -| `description` | string | No | Rich text description of the card | -| `status` | string | No | Card status: `drafted`, `published` | -| `image` | file | No | Header image for the card | -| `tag_ids` | array | No | Array of tag IDs to apply to the card | -| `last_active_at` | datetime | No | Override last activity timestamp (ISO 8601 format) | - -__Request:__ - -```json -{ - "card": { - "title": "Add dark mode support (Updated)" - } -} -``` - -__Response:__ - -Returns the updated card. - -### `DELETE /:account_slug/cards/:card_number` - -Deletes a card. Only the card creator or board administrators can delete cards. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/image` - -Removes the header image from a card. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/closure` - -Closes a card. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/closure` - -Reopens a closed card. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/not_now` - -Moves a card to "Not Now" status. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/triage` - -Moves a card into a column. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `column_id` | string | Yes | The ID of the column to move the card into | - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/triage` - -Sends a card back to triage. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/taggings` - -Toggles a tag on or off for a card. If the tag doesn't exist, it will be created. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `tag_title` | string | Yes | The title of the tag (leading `#` is stripped) | - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/assignments` - -Toggles assignment of a user to/from a card. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `assignee_id` | string | Yes | The ID of the user to assign/unassign | - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/watch` - -Subscribes the current user to notifications for this card. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/watch` - -Unsubscribes the current user from notifications for this card. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/cards/:card_number/goldness` - -Marks a card as golden. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/goldness` - -Removes golden status from a card. - -__Response:__ - -Returns `204 No Content` on success. - -## Pins - -Pins let users keep quick access to important cards. - -### `POST /:account_slug/cards/:card_number/pin` - -Pins a card for the current user. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/cards/:card_number/pin` - -Unpins a card for the current user. - -__Response:__ - -Returns `204 No Content` on success. - -### `GET /my/pins` - -Returns the current user's pinned cards. This endpoint is not paginated and returns up to 100 cards. - -__Response:__ - -```json -[ - { - "id": "03f5vaeq985jlvwv3arl4srq2", - "number": 1, - "title": "First!", - "status": "published", - "description": "Hello, World!", - "description_html": "

Hello, World!

", - "image_url": null, - "has_attachments": false, - "tags": ["programming"], - "golden": false, - "last_active_at": "2025-12-05T19:38:48.553Z", - "created_at": "2025-12-05T19:38:48.540Z", - "url": "http://fizzy.localhost:3006/897362094/cards/4", - "board": { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Fizzy", - "all_access": true, - "created_at": "2025-12-05T19:36:35.534Z", - "auto_postpone_period_in_days": 30, - "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7", - "avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar" - } - }, - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7", - "avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar" - }, - "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments" - } -] -``` - -## Comments - -Comments are attached to cards and support rich text. - -### `GET /:account_slug/cards/:card_number/comments` - -Returns a paginated list of comments on a card, sorted chronologically (oldest first). - -__Response:__ - -```json -[ - { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "created_at": "2025-12-05T19:36:35.534Z", - "updated_at": "2025-12-05T19:36:35.534Z", - "body": { - "plain_text": "This looks great!", - "html": "
This looks great!
" - }, - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "card": { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "url": "http://fizzy.localhost:3006/897362094/cards/03f5v9zo9qlcwwpyc0ascnikz" - }, - "reactions_url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions", - "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz" - } -] -``` - -### `GET /:account_slug/cards/:card_number/comments/:comment_id` - -Returns a specific comment. - -__Response:__ - -```json -{ - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "created_at": "2025-12-05T19:36:35.534Z", - "updated_at": "2025-12-05T19:36:35.534Z", - "body": { - "plain_text": "This looks great!", - "html": "
This looks great!
" - }, - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "card": { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "url": "http://fizzy.localhost:3006/897362094/cards/03f5v9zo9qlcwwpyc0ascnikz" - }, - "reactions_url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions", - "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz" -} -``` - -### `POST /:account_slug/cards/:card_number/comments` - -Creates a new comment on a card. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `body` | string | Yes | The comment body (supports rich text) | -| `created_at` | datetime | No | Override creation timestamp (ISO 8601 format) | - -__Request:__ - -```json -{ - "comment": { - "body": "This looks great!" - } -} -``` - -__Response:__ - -Returns `201 Created` with a `Location` header pointing to the new comment. - -### `PUT /:account_slug/cards/:card_number/comments/:comment_id` - -Updates a comment. Only the comment creator can update their comments. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `body` | string | Yes | The updated comment body | - -__Request:__ - -```json -{ - "comment": { - "body": "This looks even better now!" - } -} -``` - -__Response:__ - -Returns the updated comment. - -### `DELETE /:account_slug/cards/:card_number/comments/:comment_id` - -Deletes a comment. Only the comment creator can delete their comments. - -__Response:__ - -Returns `204 No Content` on success. - -## Card Reactions (Boosts) - -Card reactions (also called "boosts") let users add short responses directly to cards. These are limited to 16 characters. - -### `GET /:account_slug/cards/:card_number/reactions` - -Returns a list of reactions on a card. - -__Response:__ - -```json -[ - { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "content": "👍", - "reacter": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "url": "http://fizzy.localhost:3006/897362094/cards/3/reactions/03f5v9zo9qlcwwpyc0ascnikz" - } -] -``` - -### `POST /:account_slug/cards/:card_number/reactions` - -Adds a reaction (boost) to a card. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `content` | string | Yes | The reaction text (max 16 characters) | - -__Request:__ - -```json -{ - "reaction": { - "content": "Great 👍" - } -} -``` - -__Response:__ - -Returns `201 Created` on success. - -### `DELETE /:account_slug/cards/:card_number/reactions/:reaction_id` - -Removes your reaction from a card. Only the reaction creator can remove their own reactions. - -__Response:__ - -Returns `204 No Content` on success. - -## Comment Reactions - -Reactions are short (16-character max) responses to comments. - -### `GET /:account_slug/cards/:card_number/comments/:comment_id/reactions` - -Returns a list of reactions on a comment. - -__Response:__ - -```json -[ - { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "content": "👍", - "reacter": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions/03f5v9zo9qlcwwpyc0ascnikz" - } -] -``` - -### `POST /:account_slug/cards/:card_number/comments/:comment_id/reactions` - -Adds a reaction to a comment. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `content` | string | Yes | The reaction text | - -__Request:__ - -```json -{ - "reaction": { - "content": "Great 👍" - } -} -``` - -__Response:__ - -Returns `201 Created` on success. - -### `DELETE /:account_slug/cards/:card_number/comments/:comment_id/reactions/:reaction_id` - -Removes your reaction from a comment. - -__Response:__ - -Returns `204 No Content` on success. - -## Steps - -Steps are to-do items on a card. - -### `GET /:account_slug/cards/:card_number/steps/:step_id` - -Returns a specific step. - -__Response:__ - -```json -{ - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "content": "Write tests", - "completed": false -} -``` - -### `POST /:account_slug/cards/:card_number/steps` - -Creates a new step on a card. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `content` | string | Yes | The step text | -| `completed` | boolean | No | Whether the step is completed (default: `false`) | - -__Request:__ - -```json -{ - "step": { - "content": "Write tests" - } -} -``` - -__Response:__ - -Returns `201 Created` with a `Location` header pointing to the new step. - -### `PUT /:account_slug/cards/:card_number/steps/:step_id` - -Updates a step. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `content` | string | No | The step text | -| `completed` | boolean | No | Whether the step is completed | - -__Request:__ - -```json -{ - "step": { - "completed": true - } -} -``` - -__Response:__ - -Returns the updated step. - -### `DELETE /:account_slug/cards/:card_number/steps/:step_id` - -Deletes a step. - -__Response:__ - -Returns `204 No Content` on success. - -## Tags - -Tags are labels that can be applied to cards for organization and filtering. - -### `GET /:account_slug/tags` - -Returns a list of all tags in the account, sorted alphabetically. - -__Response:__ - -```json -[ - { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "title": "bug", - "created_at": "2025-12-05T19:36:35.534Z", - "url": "http://fizzy.localhost:3006/897362094/cards?tag_ids[]=03f5v9zo9qlcwwpyc0ascnikz" - }, - { - "id": "03f5v9zo9qlcwwpyc0ascnilz", - "title": "feature", - "created_at": "2025-12-05T19:36:35.534Z", - "url": "http://fizzy.localhost:3006/897362094/cards?tag_ids[]=03f5v9zo9qlcwwpyc0ascnilz" - } -] -``` - -## Columns - -Columns represent stages in a workflow on a board. Cards move through columns as they progress. - -### `GET /:account_slug/boards/:board_id/columns` - -Returns a list of columns on a board, sorted by position. - -__Response:__ - -```json -[ - { - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "Recording", - "color": "var(--color-card-default)", - "created_at": "2025-12-05T19:36:35.534Z" - }, - { - "id": "03f5v9zkft4hj9qq0lsn9ohcn", - "name": "Published", - "color": "var(--color-card-4)", - "created_at": "2025-12-05T19:36:35.534Z" - } -] -``` - -### `GET /:account_slug/boards/:board_id/columns/:column_id` - -Returns the specified column. - -__Response:__ - -```json -{ - "id": "03f5v9zkft4hj9qq0lsn9ohcm", - "name": "In Progress", - "color": "var(--color-card-default)", - "created_at": "2025-12-05T19:36:35.534Z" -} -``` - -### `POST /:account_slug/boards/:board_id/columns` - -Creates a new column on the board. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `name` | string | Yes | The name of the column | -| `color` | string | No | The column color. One of: `var(--color-card-default)` (Blue), `var(--color-card-1)` (Gray), `var(--color-card-2)` (Tan), `var(--color-card-3)` (Yellow), `var(--color-card-4)` (Lime), `var(--color-card-5)` (Aqua), `var(--color-card-6)` (Violet), `var(--color-card-7)` (Purple), `var(--color-card-8)` (Pink) | - -__Request:__ - -```json -{ - "column": { - "name": "In Progress", - "color": "var(--color-card-4)" - } -} -``` - -__Response:__ - -Returns `201 Created` with a `Location` header pointing to the new column. - -### `PUT /:account_slug/boards/:board_id/columns/:column_id` - -Updates a column. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `name` | string | No | The name of the column | -| `color` | string | No | The column color | - -__Request:__ - -```json -{ - "column": { - "name": "Done" - } -} -``` - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/boards/:board_id/columns/:column_id` - -Deletes a column. - -__Response:__ - -Returns `204 No Content` on success. - -## Users - -Users represent people who have access to an account. - -### `GET /:account_slug/users` - -Returns a list of active users in the account. - -__Response:__ - -```json -[ - { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - { - "id": "03f5v9zjysoy0fqs9yg0ei3hq", - "name": "Jason Fried", - "role": "member", - "active": true, - "email_address": "jason@example.com", - "created_at": "2025-12-05T19:36:35.419Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjysoy0fqs9yg0ei3hq" - }, - { - "id": "03f5v9zk1dtqduod5bkhv3k8m", - "name": "Jason Zimdars", - "role": "member", - "active": true, - "email_address": "jz@example.com", - "created_at": "2025-12-05T19:36:35.435Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zk1dtqduod5bkhv3k8m" - }, - { - "id": "03f5v9zk3nw9ja92e7s4h2wbe", - "name": "Kevin Mcconnell", - "role": "member", - "active": true, - "email_address": "kevin@example.com", - "created_at": "2025-12-05T19:36:35.451Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zk3nw9ja92e7s4h2wbe" - } -] -``` - -### `GET /:account_slug/users/:user_id` - -Returns the specified user. - -__Response:__ - -```json -{ - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" -} -``` - -### `PUT /:account_slug/users/:user_id` - -Updates a user. You can only update users you have permission to change. - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `name` | string | No | The user's display name | -| `avatar` | file | No | The user's avatar image | - -__Request:__ - -```json -{ - "user": { - "name": "David H. Hansson" - } -} -``` - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/users/:user_id` - -Deactivates a user. You can only deactivate users you have permission to change. - -__Response:__ - -Returns `204 No Content` on success. - -## Notifications - -Notifications inform users about events that happened in the account, such as comments, assignments, and card updates. - -### `GET /:account_slug/notifications` - -Returns a list of notifications for the current user. Unread notifications are returned first, followed by read notifications. - -__Response:__ - -```json -[ - { - "id": "03f5va03bpuvkcjemcxl73ho2", - "read": false, - "read_at": null, - "created_at": "2025-11-19T04:03:58.000Z", - "title": "Plain text mentions", - "body": "Assigned to self", - "creator": { - "id": "03f5v9zjw7pz8717a4no1h8a7", - "name": "David Heinemeier Hansson", - "role": "owner", - "active": true, - "email_address": "david@example.com", - "created_at": "2025-12-05T19:36:35.401Z", - "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" - }, - "card": { - "id": "03f5v9zo9qlcwwpyc0ascnikz", - "title": "Plain text mentions", - "status": "published", - "url": "http://fizzy.localhost:3006/897362094/cards/3" - }, - "url": "http://fizzy.localhost:3006/897362094/notifications/03f5va03bpuvkcjemcxl73ho2" - } -] -``` - -### `POST /:account_slug/notifications/:notification_id/reading` - -Marks a notification as read. - -__Response:__ - -Returns `204 No Content` on success. - -### `DELETE /:account_slug/notifications/:notification_id/reading` - -Marks a notification as unread. - -__Response:__ - -Returns `204 No Content` on success. - -### `POST /:account_slug/notifications/bulk_reading` - -Marks all unread notifications as read. - -__Response:__ - -Returns `204 No Content` on success. diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 000000000..92d5fe2c2 --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,148 @@ +# Fizzy API + +Fizzy has an API that allows you to integrate your application with it or to create +a bot to perform various actions for you. + +## API Endpoints + +- [Authentication](sections/authentication.md) +- [Identity](sections/identity.md) +- [Account](sections/account.md) +- [Boards](sections/boards.md) +- [Columns](sections/columns.md) +- [Cards](sections/cards.md) +- [Pins](sections/pins.md) +- [Steps](sections/steps.md) +- [Comments](sections/comments.md) +- [Reactions](sections/reactions.md) +- [Tags](sections/tags.md) +- [Users](sections/users.md) +- [Notifications](sections/notifications.md) +- [Rich Text](sections/rich_text.md) +- [Webhooks](sections/webhooks.md) + +## Authentication + +There are two ways to authenticate with the Fizzy API: + +1. **Personal access tokens** - Long-lived tokens for scripts and integrations +2. **Magic link authentication** - Session-based authentication for native apps + +Read the [authentication guide](sections/authentication.md) to get started. + +## Caching + +Most endpoints return [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag) and [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control) headers. You can use these to avoid re-downloading unchanged data. + +### Using ETags + +When you make a request, the response includes an `ETag` header: + +``` +HTTP/1.1 200 OK +ETag: "abc123" +Cache-Control: max-age=0, private, must-revalidate +``` + +On subsequent requests, include the ETag value in the `If-None-Match` header: + +``` +GET /1234567/cards/42.json +If-None-Match: "abc123" +``` + +If the resource hasn't changed, you'll receive a `304 Not Modified` response with no body, saving bandwidth and processing time: + +``` +HTTP/1.1 304 Not Modified +ETag: "abc123" +``` + +If the resource has changed, you'll receive the full response with a new ETag. + +__Example in Ruby:__ + +```ruby +# Store the ETag from the response +etag = response.headers["ETag"] + +# On next request, send it back +headers = { "If-None-Match" => etag } +response = client.get("/1234567/cards/42.json", headers: headers) + +if response.status == 304 + # Nothing to do, the card hasn't changed +else + # The card has changed, process the new data +end +``` + +## Error Responses + +When a request fails, the API response will communicate the source of the problem through the HTTP status code. + +| Status Code | Description | +|-------------|-------------| +| `400 Bad Request` | The request was malformed or missing required parameters | +| `401 Unauthorized` | Authentication failed or access token is invalid | +| `403 Forbidden` | You don't have permission to perform this action | +| `404 Not Found` | The requested resource doesn't exist or you don't have access to it | +| `422 Unprocessable Entity` | Validation failed (see error response format above) | +| `500 Internal Server Error` | An unexpected error occurred on the server | + +If a request contains invalid data for fields, such as entering a string into a number field, in most cases the API will respond with a `500 Internal Server Error`. Clients are expected to perform some validation on their end before making a request. + +A validation error will produce a `422 Unprocessable Entity` response, which will sometimes be accompanied by details about the error: + +```json +{ + "avatar": ["must be a JPEG, PNG, GIF, or WebP image"] +} +``` + +## Pagination + +All endpoints that return a list of items are paginated. The page size can vary from endpoint to endpoint, +and we use a dynamic page size where initial pages return fewer results than later pages. + +If there are more results to fetch, the response will include a `Link` header with a `rel="next"` link to the next page of results: + +```bash +curl -H "Authorization: Bearer put-your-access-token-here" -H "Accept: application/json" -v http://fizzy.localhost:3006/686465299/cards +# ... +< link: ; rel="next" +# ... +``` + +## List parameters + +When an endpoint accepts a list of values as a parameter, you can provide multiple values by repeating the parameter name: + +``` +?tag_ids[]=tag1&tag_ids[]=tag2&tag_ids[]=tag3 +``` + +List parameters always end with `[]`. + +## File Uploads + +Some endpoints accept file uploads. To upload a file, send a `multipart/form-data` request instead of JSON. +You can combine file uploads with other parameters in the same request. + +__Example using curl:__ + +```bash +curl -X PUT \ + -H "Authorization: Bearer put-your-access-token-here" \ + -F "user[name]=David H. Hansson" \ + -F "user[avatar]=@/path/to/avatar.jpg" \ + http://fizzy.localhost:3006/686465299/users/03f5v9zjw7pz8717a4no1h8a7 +``` + +## Rich Text Fields + +Some fields accept rich text content. These fields accept HTML input, which will be sanitized to remove unsafe tags and attributes. + +See the [rich text guide](sections/rich_text.md) for more information, including how to attach files to rich text fields using the direct upload flow. + + diff --git a/docs/api/sections/account.md b/docs/api/sections/account.md new file mode 100644 index 000000000..2b1a9a4f5 --- /dev/null +++ b/docs/api/sections/account.md @@ -0,0 +1,65 @@ +# Account + +## `GET /account/settings` + +Returns the current account. + +__Response:__ + +```json +{ + "id": "03f5v9zjvypwh0t0e2rfh0h7k", + "name": "37signals", + "cards_count": 5, + "created_at": "2025-12-05T19:36:35.401Z", + "auto_postpone_period_in_days": 30 +} +``` + +The `auto_postpone_period_in_days` is the account-level default in days (e.g. `30`). Cards are automatically moved to "Not Now" after this period of inactivity. Each board can override this with its own value. + +## `PUT /account/entropy` + +Updates the account-level default auto close period. Requires admin role. + +__Request:__ + +```json +{ + "entropy": { + "auto_postpone_period_in_days": 30 + } +} +``` + +__Response:__ + +Returns the account object: + +```json +{ + "id": "03f5v9zjvypwh0t0e2rfh0h7k", + "name": "37signals", + "cards_count": 5, + "created_at": "2025-12-05T19:36:35.401Z", + "auto_postpone_period_in_days": 30 +} +``` + +## `PUT /:account_slug/boards/:board_id/entropy` + +Updates the auto close period for a specific board. Requires board admin permission. + +__Request:__ + +```json +{ + "board": { + "auto_postpone_period_in_days": 90 + } +} +``` + +__Response:__ + +Returns the board object. diff --git a/docs/api/sections/authentication.md b/docs/api/sections/authentication.md new file mode 100644 index 000000000..1030597b3 --- /dev/null +++ b/docs/api/sections/authentication.md @@ -0,0 +1,172 @@ +# Authentication + +There are two ways to authenticate with the Fizzy API: + +1. **Personal access tokens** - Long-lived tokens for scripts and integrations +2. **Magic link authentication** - Session-based authentication for native apps + +## Personal Access Tokens + +To use the API you'll need an access token. To get one, go to your profile, then, +in the API section, click on "Personal access tokens" and then click on +"Generate new access token". + +Give it a description and pick what kind of permission you want the access token to have: +- `Read`: allows reading data from your account +- `Read + Write`: allows reading and writing data to your account on your behalf + +Then click on "Generate access token". + +
+ Access token generation guide with screenshots + + | Step | Description | Screenshot | + |:----:|-------------|:----------:| + | 1 | Go to your profile | Profile page with API section | + | 2 | In the API section click on "Personal access token" | Personal access tokens page | + | 3 | Click on "Generate a new access token" | Generate new access token dialog | + | 4 | Give it a description and assign it a permission | Access token created | +
+ +> [!IMPORTANT] +> __An access token is like a password, keep it secret and do not share it with anyone.__ +> Any person or application that has your access token can perform actions on your behalf. + +To authenticate a request using your access token, include it in the `Authorization` header: + +```bash +curl -H "Authorization: Bearer put-your-access-token-here" -H "Accept: application/json" https://app.fizzy.do/my/identity +``` + +## Magic Link Authentication + +For native apps, you can authenticate users via magic links. This is a two-step process: + +### 1. Request a magic link + +Send the user's email address to request a magic link be sent to them: + +```bash +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{"email_address": "user@example.com"}' \ + https://app.fizzy.do/session +``` + +__Response:__ + +``` +HTTP/1.1 201 Created +Set-Cookie: pending_authentication_token=...; HttpOnly; SameSite=Lax +``` + +```json +{ + "pending_authentication_token": "eyJfcmFpbHMi..." +} +``` + +The response includes a `pending_authentication_token` both in the JSON body and as a cookie. +Native apps should store this token and include it as a cookie when submitting the magic link code. + +__Error responses:__ + +| Status Code | Description | +|--------|-------------| +| `422 Unprocessable entity` | Invalid email address, if sign ups are enabled and the value isn't a valid email address | +| `429 Too Many Requests` | Rate limit exceeded | + +### 2. Submit the magic link code + +Once the user receives the magic link email, they'll have a 6-character code. Submit it to complete authentication: + +```bash +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Cookie: pending_authentication_token=eyJfcmFpbHMi..." \ + -d '{"code": "ABC123"}' \ + https://app.fizzy.do/session/magic_link +``` + +__Response:__ + +```json +{ + "session_token": "eyJfcmFpbHMi..." +} +``` + +The `session_token` can be used to authenticate subsequent requests by including it as a cookie: + +```bash +curl -H "Cookie: session_token=eyJfcmFpbHMi..." \ + -H "Accept: application/json" \ + https://app.fizzy.do/my/identity +``` + +__Error responses:__ + +| Status Code | Description | +|--------|-------------| +| `401 Unauthorized` | Invalid `pending_authentication_token` or `code` | +| `429 Too Many Requests` | Rate limit exceeded | + + +### Delete server-side session (_log out_) + +To log out and destroy the server-side session: + +```bash +curl -X DELETE \ + -H "Accept: application/json" \ + -H "Cookie: session_token=eyJfcmFpbHMi..." \ + https://app.fizzy.do/session +``` + +__Response:__ + +Returns `204 No Content` on success. + +### Create an access token via the API + +You can programmatically create a personal access token using either a session cookie or an existing Bearer token: + +```bash +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Cookie: session_token=eyJfcmFpbHMi..." \ + -d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \ + https://app.fizzy.do/1234567/my/access_tokens +``` + +Or with a Bearer token (must have `write` permission): + +```bash +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer put-your-access-token-here" \ + -d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \ + https://app.fizzy.do/1234567/my/access_tokens +``` + +The `permission` field accepts `read` or `write`. + +__Response:__ + +``` +HTTP/1.1 201 Created +``` + +```json +{ + "token": "4f9Q6d2wXr8Kp1Ls0Vz3BnTa", + "description": "Fizzy CLI", + "permission": "write" +} +``` + +Store the `token` value securely — it won't be retrievable again. Use it as a Bearer token for subsequent API requests. diff --git a/docs/api/sections/boards.md b/docs/api/sections/boards.md new file mode 100644 index 000000000..bf77245ab --- /dev/null +++ b/docs/api/sections/boards.md @@ -0,0 +1,176 @@ +# Boards + +Boards are where you organize your work - they contain your cards. + +## `GET /:account_slug/boards` + +Returns a list of boards that you can access in the specified account. + +__Response:__ + +```json +[ + { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + } + } +] +``` + +## `GET /:account_slug/boards/:board_id` + +Returns the specified board. + +__Response:__ + +```json +{ + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "public_url": "http://fizzy.localhost:3006/897362094/public/boards/aB3dEfGhIjKlMnOp" +} +``` + +The `public_url` field is only present when the board is published. + +## `POST /:account_slug/boards` + +Creates a new Board in the account. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | The name of the board | +| `all_access` | boolean | No | Whether any user in the account can access this board. Defaults to `true` | +| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | +| `public_description` | string | No | Rich text description shown on the public board page | + +__Request:__ + +```json +{ + "board": { + "name": "My new board" + } +} +``` + +__Response:__ + +Returns `201 Created` with a `Location` header pointing to the new board: + +``` +HTTP/1.1 201 Created +Location: /897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm.json +``` + +## `PUT /:account_slug/boards/:board_id` + +Updates a Board. Only board administrators can update a board. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | No | The name of the board | +| `all_access` | boolean | No | Whether any user in the account can access this board | +| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | +| `public_description` | string | No | Rich text description shown on the public board page | +| `user_ids` | array | No | Array of *all* user IDs who should have access to this board (only applicable when `all_access` is `false`) | + +__Request:__ + +```json +{ + "board": { + "name": "Updated board name", + "auto_postpone_period_in_days": 30, + "public_description": "This is a **public** description of the board.", + "all_access": false, + "user_ids": [ + "03f5v9zppzlksuj4mxba2nbzn", + "03f5v9zjw7pz8717a4no1h8a7" + ] + } +} +``` + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/boards/:board_id` + +Deletes a Board. Only board administrators can delete a board. + +__Response:__ + +Returns `204 No Content` on success. + +## Board Publications + +Publishing a board makes it publicly accessible via a shareable link, without requiring authentication. Only board administrators can publish or unpublish a board. + +## `POST /:account_slug/boards/:board_id/publication` + +Publishes a board, generating a shareable public link. + +__Response:__ + +``` +HTTP/1.1 201 Created +``` + +```json +{ + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "public_url": "http://fizzy.localhost:3006/897362094/public/boards/aB3dEfGhIjKlMnOp" +} +``` + +If the board is already published, the existing publication is returned. + +## `DELETE /:account_slug/boards/:board_id/publication` + +Unpublishes a board, removing public access. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/cards.md b/docs/api/sections/cards.md new file mode 100644 index 000000000..83ca51253 --- /dev/null +++ b/docs/api/sections/cards.md @@ -0,0 +1,322 @@ +# Cards + +Cards are tasks or items of work on a board. They can be organized into columns, tagged, assigned to users, and have comments. + +## `GET /:account_slug/cards` + +Returns a paginated list of cards you have access to. Results can be filtered using query parameters. + +__Query Parameters:__ + +| Parameter | Description | +|-----------|-------------| +| `board_ids[]` | Filter by board ID(s) | +| `tag_ids[]` | Filter by tag ID(s) | +| `assignee_ids[]` | Filter by assignee user ID(s) | +| `creator_ids[]` | Filter by card creator ID(s) | +| `closer_ids[]` | Filter by user ID(s) who closed the cards | +| `card_ids[]` | Filter to specific card ID(s) | +| `indexed_by` | Filter by: `all` (default), `closed`, `not_now`, `stalled`, `postponing_soon`, `golden` | +| `sorted_by` | Sort order: `latest` (default), `newest`, `oldest` | +| `assignment_status` | Filter by assignment status: `unassigned` | +| `creation` | Filter by creation date: `today`, `yesterday`, `thisweek`, `lastweek`, `thismonth`, `lastmonth`, `thisyear`, `lastyear` | +| `closure` | Filter by closure date: `today`, `yesterday`, `thisweek`, `lastweek`, `thismonth`, `lastmonth`, `thisyear`, `lastyear` | +| `terms[]` | Search terms to filter cards | + +__Response:__ + +```json +[ + { + "id": "03f5vaeq985jlvwv3arl4srq2", + "number": 1, + "title": "First!", + "status": "published", + "description": "Hello, World!", + "description_html": "

Hello, World!

", + "image_url": null, + "has_attachments": false, + "tags": ["programming"], + "golden": false, + "last_active_at": "2025-12-05T19:38:48.553Z", + "created_at": "2025-12-05T19:38:48.540Z", + "url": "http://fizzy.localhost:3006/897362094/cards/4", + "board": { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + } + }, + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments", + "reactions_url": "http://fizzy.localhost:3006/897362094/cards/4/reactions" + } +] +``` + +## `GET /:account_slug/cards/:card_number` + +Returns a specific card by its number. + +__Response:__ + +```json +{ + "id": "03f5vaeq985jlvwv3arl4srq2", + "number": 1, + "title": "First!", + "status": "published", + "description": "Hello, World!", + "description_html": "

Hello, World!

", + "image_url": null, + "has_attachments": false, + "tags": ["programming"], + "closed": false, + "golden": false, + "last_active_at": "2025-12-05T19:38:48.553Z", + "created_at": "2025-12-05T19:38:48.540Z", + "url": "http://fizzy.localhost:3006/897362094/cards/4", + "board": { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + } + }, + "column": { + "id": "03f5v9zkft4hj9qq0lsn9ohcn", + "name": "In Progress", + "color": { + "name": "Lime", + "value": "var(--color-card-4)" + }, + "created_at": "2025-12-05T19:36:35.534Z" + }, + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments", + "reactions_url": "http://fizzy.localhost:3006/897362094/cards/4/reactions", + "steps": [ + { + "id": "03f8huu0sog76g3s975963b5e", + "content": "This is the first step", + "completed": false + }, + { + "id": "03f8huu0sog76g3s975969734", + "content": "This is the second step", + "completed": false + } + ] +} +``` + +> **Note:** The `closed` field indicates whether the card is in the "Done" state. The `column` field is only present when the card has been triaged into a column; cards in "Maybe?", "Not Now" or "Done" will not have this field. + +## `POST /:account_slug/boards/:board_id/cards` + +Creates a new card in a board. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `title` | string | Yes | The title of the card | +| `description` | string | No | Rich text description of the card | +| `status` | string | No | Initial status: `published` (default), `drafted` | +| `image` | file | No | Header image for the card | +| `tag_ids` | array | No | Array of tag IDs to apply to the card | +| `created_at` | datetime | No | Override creation timestamp (ISO 8601 format) | +| `last_active_at` | datetime | No | Override last activity timestamp (ISO 8601 format) | + +__Request:__ + +```json +{ + "card": { + "title": "Add dark mode support", + "description": "We need to add dark mode to the app" + } +} +``` + +__Response:__ + +Returns `201 Created` with a `Location` header pointing to the new card. + +## `PUT /:account_slug/cards/:card_number` + +Updates a card. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `title` | string | No | The title of the card | +| `description` | string | No | Rich text description of the card | +| `status` | string | No | Card status: `drafted`, `published` | +| `image` | file | No | Header image for the card | +| `tag_ids` | array | No | Array of tag IDs to apply to the card | +| `last_active_at` | datetime | No | Override last activity timestamp (ISO 8601 format) | + +__Request:__ + +```json +{ + "card": { + "title": "Add dark mode support (Updated)" + } +} +``` + +__Response:__ + +Returns the updated card. + +## `DELETE /:account_slug/cards/:card_number` + +Deletes a card. Only the card creator or board administrators can delete cards. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/image` + +Removes the header image from a card. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/closure` + +Closes a card. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/closure` + +Reopens a closed card. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/not_now` + +Moves a card to "Not Now" status. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/triage` + +Moves a card into a column. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `column_id` | string | Yes | The ID of the column to move the card into | + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/triage` + +Sends a card back to triage. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/taggings` + +Toggles a tag on or off for a card. If the tag doesn't exist, it will be created. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `tag_title` | string | Yes | The title of the tag (leading `#` is stripped) | + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/assignments` + +Toggles assignment of a user to/from a card. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `assignee_id` | string | Yes | The ID of the user to assign/unassign | + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/watch` + +Subscribes the current user to notifications for this card. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/watch` + +Unsubscribes the current user from notifications for this card. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/cards/:card_number/goldness` + +Marks a card as golden. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/goldness` + +Removes golden status from a card. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/columns.md b/docs/api/sections/columns.md new file mode 100644 index 000000000..73bd1c728 --- /dev/null +++ b/docs/api/sections/columns.md @@ -0,0 +1,96 @@ +# Columns + +Columns represent stages in a workflow on a board. Cards move through columns as they progress. + +## `GET /:account_slug/boards/:board_id/columns` + +Returns a list of columns on a board, sorted by position. + +__Response:__ + +```json +[ + { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Recording", + "color": "var(--color-card-default)", + "created_at": "2025-12-05T19:36:35.534Z" + }, + { + "id": "03f5v9zkft4hj9qq0lsn9ohcn", + "name": "Published", + "color": "var(--color-card-4)", + "created_at": "2025-12-05T19:36:35.534Z" + } +] +``` + +## `GET /:account_slug/boards/:board_id/columns/:column_id` + +Returns the specified column. + +__Response:__ + +```json +{ + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "In Progress", + "color": "var(--color-card-default)", + "created_at": "2025-12-05T19:36:35.534Z" +} +``` + +## `POST /:account_slug/boards/:board_id/columns` + +Creates a new column on the board. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | The name of the column | +| `color` | string | No | The column color. One of: `var(--color-card-default)` (Blue), `var(--color-card-1)` (Gray), `var(--color-card-2)` (Tan), `var(--color-card-3)` (Yellow), `var(--color-card-4)` (Lime), `var(--color-card-5)` (Aqua), `var(--color-card-6)` (Violet), `var(--color-card-7)` (Purple), `var(--color-card-8)` (Pink) | + +__Request:__ + +```json +{ + "column": { + "name": "In Progress", + "color": "var(--color-card-4)" + } +} +``` + +__Response:__ + +Returns `201 Created` with a `Location` header pointing to the new column. + +## `PUT /:account_slug/boards/:board_id/columns/:column_id` + +Updates a column. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | No | The name of the column | +| `color` | string | No | The column color | + +__Request:__ + +```json +{ + "column": { + "name": "Done" + } +} +``` + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/boards/:board_id/columns/:column_id` + +Deletes a column. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/comments.md b/docs/api/sections/comments.md new file mode 100644 index 000000000..a0854acc2 --- /dev/null +++ b/docs/api/sections/comments.md @@ -0,0 +1,124 @@ +# Comments + +Comments are attached to cards and support rich text. + +## `GET /:account_slug/cards/:card_number/comments` + +Returns a paginated list of comments on a card, sorted chronologically (oldest first). + +__Response:__ + +```json +[ + { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "created_at": "2025-12-05T19:36:35.534Z", + "updated_at": "2025-12-05T19:36:35.534Z", + "body": { + "plain_text": "This looks great!", + "html": "
This looks great!
" + }, + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "card": { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "url": "http://fizzy.localhost:3006/897362094/cards/03f5v9zo9qlcwwpyc0ascnikz" + }, + "reactions_url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions", + "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz" + } +] +``` + +## `GET /:account_slug/cards/:card_number/comments/:comment_id` + +Returns a specific comment. + +__Response:__ + +```json +{ + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "created_at": "2025-12-05T19:36:35.534Z", + "updated_at": "2025-12-05T19:36:35.534Z", + "body": { + "plain_text": "This looks great!", + "html": "
This looks great!
" + }, + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "card": { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "url": "http://fizzy.localhost:3006/897362094/cards/03f5v9zo9qlcwwpyc0ascnikz" + }, + "reactions_url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions", + "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz" +} +``` + +## `POST /:account_slug/cards/:card_number/comments` + +Creates a new comment on a card. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `body` | string | Yes | The comment body (supports rich text) | +| `created_at` | datetime | No | Override creation timestamp (ISO 8601 format) | + +__Request:__ + +```json +{ + "comment": { + "body": "This looks great!" + } +} +``` + +__Response:__ + +Returns `201 Created` with a `Location` header pointing to the new comment. + +## `PUT /:account_slug/cards/:card_number/comments/:comment_id` + +Updates a comment. Only the comment creator can update their comments. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `body` | string | Yes | The updated comment body | + +__Request:__ + +```json +{ + "comment": { + "body": "This looks even better now!" + } +} +``` + +__Response:__ + +Returns the updated comment. + +## `DELETE /:account_slug/cards/:card_number/comments/:comment_id` + +Deletes a comment. Only the comment creator can delete their comments. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/identity.md b/docs/api/sections/identity.md new file mode 100644 index 000000000..5167aaf95 --- /dev/null +++ b/docs/api/sections/identity.md @@ -0,0 +1,46 @@ +# Identity + +An Identity represents a person using Fizzy. + +## `GET /my/identity` + +Returns a list of accounts the identity has access to, including the user for each account. + +__Response:__ + +```json +{ + "accounts": [ + { + "id": "03f5v9zjskhcii2r45ih3u1rq", + "name": "37signals", + "slug": "/897362094", + "created_at": "2025-12-05T19:36:35.377Z", + "user": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/users/03f5v9zjw7pz8717a4no1h8a7" + } + }, + { + "id": "03f5v9zpko7mmhjzwum3youpp", + "name": "Honcho", + "slug": "/686465299", + "created_at": "2025-12-05T19:36:36.746Z", + "user": { + "id": "03f5v9zppzlksuj4mxba2nbzn", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:36.783Z", + "url": "http://fizzy.localhost:3006/users/03f5v9zppzlksuj4mxba2nbzn" + } + } + ] +} +``` diff --git a/docs/api/sections/notifications.md b/docs/api/sections/notifications.md new file mode 100644 index 000000000..891d3a86a --- /dev/null +++ b/docs/api/sections/notifications.md @@ -0,0 +1,62 @@ +# Notifications + +Notifications inform users about events that happened in the account, such as comments, assignments, and card updates. + +## `GET /:account_slug/notifications` + +Returns a list of notifications for the current user. Unread notifications are returned first, followed by read notifications. + +__Response:__ + +```json +[ + { + "id": "03f5va03bpuvkcjemcxl73ho2", + "read": false, + "read_at": null, + "created_at": "2025-11-19T04:03:58.000Z", + "title": "Plain text mentions", + "body": "Assigned to self", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "card": { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "title": "Plain text mentions", + "status": "published", + "url": "http://fizzy.localhost:3006/897362094/cards/3" + }, + "url": "http://fizzy.localhost:3006/897362094/notifications/03f5va03bpuvkcjemcxl73ho2" + } +] +``` + +## `POST /:account_slug/notifications/:notification_id/reading` + +Marks a notification as read. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/notifications/:notification_id/reading` + +Marks a notification as unread. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/notifications/bulk_reading` + +Marks all unread notifications as read. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/pins.md b/docs/api/sections/pins.md new file mode 100644 index 000000000..0df248698 --- /dev/null +++ b/docs/api/sections/pins.md @@ -0,0 +1,74 @@ +# Pins + +Pins let users keep quick access to important cards. + +## `POST /:account_slug/cards/:card_number/pin` + +Pins a card for the current user. + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/cards/:card_number/pin` + +Unpins a card for the current user. + +__Response:__ + +Returns `204 No Content` on success. + +## `GET /my/pins` + +Returns the current user's pinned cards. This endpoint is not paginated and returns up to 100 cards. + +__Response:__ + +```json +[ + { + "id": "03f5vaeq985jlvwv3arl4srq2", + "number": 1, + "title": "First!", + "status": "published", + "description": "Hello, World!", + "description_html": "

Hello, World!

", + "image_url": null, + "has_attachments": false, + "tags": ["programming"], + "golden": false, + "last_active_at": "2025-12-05T19:38:48.553Z", + "created_at": "2025-12-05T19:38:48.540Z", + "url": "http://fizzy.localhost:3006/897362094/cards/4", + "board": { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7", + "avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar" + } + }, + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7", + "avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar" + }, + "comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments" + } +] +``` diff --git a/docs/api/sections/reactions.md b/docs/api/sections/reactions.md new file mode 100644 index 000000000..7ad7b5729 --- /dev/null +++ b/docs/api/sections/reactions.md @@ -0,0 +1,119 @@ +# Reactions + +## Card Reactions (Boosts) + +Card reactions (also called "boosts") let users add short responses directly to cards. These are limited to 16 characters. + +## `GET /:account_slug/cards/:card_number/reactions` + +Returns a list of reactions on a card. + +__Response:__ + +```json +[ + { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "content": "👍", + "reacter": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "url": "http://fizzy.localhost:3006/897362094/cards/3/reactions/03f5v9zo9qlcwwpyc0ascnikz" + } +] +``` + +## `POST /:account_slug/cards/:card_number/reactions` + +Adds a reaction (boost) to a card. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `content` | string | Yes | The reaction text (max 16 characters) | + +__Request:__ + +```json +{ + "reaction": { + "content": "Great 👍" + } +} +``` + +__Response:__ + +Returns `201 Created` on success. + +## `DELETE /:account_slug/cards/:card_number/reactions/:reaction_id` + +Removes your reaction from a card. Only the reaction creator can remove their own reactions. + +__Response:__ + +Returns `204 No Content` on success. + +## Comment Reactions + +Reactions are short (16-character max) responses to comments. + +## `GET /:account_slug/cards/:card_number/comments/:comment_id/reactions` + +Returns a list of reactions on a comment. + +__Response:__ + +```json +[ + { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "content": "👍", + "reacter": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "url": "http://fizzy.localhost:3006/897362094/cards/3/comments/03f5v9zo9qlcwwpyc0ascnikz/reactions/03f5v9zo9qlcwwpyc0ascnikz" + } +] +``` + +## `POST /:account_slug/cards/:card_number/comments/:comment_id/reactions` + +Adds a reaction to a comment. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `content` | string | Yes | The reaction text | + +__Request:__ + +```json +{ + "reaction": { + "content": "Great 👍" + } +} +``` + +__Response:__ + +Returns `201 Created` on success. + +## `DELETE /:account_slug/cards/:card_number/comments/:comment_id/reactions/:reaction_id` + +Removes your reaction from a comment. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/rich_text.md b/docs/api/sections/rich_text.md new file mode 100644 index 000000000..18cbb28f7 --- /dev/null +++ b/docs/api/sections/rich_text.md @@ -0,0 +1,86 @@ +# Rich Text Fields + +Some fields accept rich text content. These fields accept HTML input, which will be sanitized to remove unsafe tags and attributes. + +```json +{ + "card": { + "title": "My card", + "description": "

This is bold and this is italic.

  • Item 1
  • Item 2
" + } +} +``` + +## Attaching files to rich text + +To attach files (images, documents) to rich text fields, use ActionText's direct upload flow: + +### 1. Create a direct upload + +First, request a direct upload URL by sending file metadata: + +```bash +curl -X POST \ + -H "Authorization: Bearer put-your-access-token-here" \ + -H "Content-Type: application/json" \ + -d '{ + "blob": { + "filename": "screenshot.png", + "byte_size": 12345, + "checksum": "GQ5SqLsM7ylnji0Wgd9wNA==", + "content_type": "image/png" + } + }' \ + https://app.fizzy.do/123456/rails/active_storage/direct_uploads +``` + +The `checksum` is a Base64-encoded MD5 hash of the file content. +The direct upload endpoint is scoped to your account (replace `/123456` with your account slug). + +__Response:__ + +```json +{ + "id": "abc123", + "key": "abc123def456", + "filename": "screenshot.png", + "content_type": "image/png", + "byte_size": 12345, + "checksum": "GQ5SqLsM7ylnji0Wgd9wNA==", + "direct_upload": { + "url": "https://storage.example.com/...", + "headers": { + "Content-Type": "image/png", + "Content-MD5": "GQ5SqLsM7ylnji0Wgd9wNA==" + } + }, + "signed_id": "eyJfcmFpbHMi..." +} +``` + +### 2. Upload the file + +Upload the file directly to the provided URL with the specified headers: + +```bash +curl -X PUT \ + -H "Content-Type: image/png" \ + -H "Content-MD5: GQ5SqLsM7ylnji0Wgd9wNA==" \ + --data-binary @screenshot.png \ + "https://storage.example.com/..." +``` + +### 3. Reference the file in rich text + +Use the `signed_id` from step 1 to embed the file in your rich text using an `` tag: + +```json +{ + "card": { + "title": "Card with image", + "description": "

Here's a screenshot:

" + } +} +``` + +The `sgid` attribute should contain the `signed_id` returned from the direct upload response. diff --git a/docs/api/sections/steps.md b/docs/api/sections/steps.md new file mode 100644 index 000000000..668957d37 --- /dev/null +++ b/docs/api/sections/steps.md @@ -0,0 +1,71 @@ +# Steps + +Steps are to-do items on a card. + +## `GET /:account_slug/cards/:card_number/steps/:step_id` + +Returns a specific step. + +__Response:__ + +```json +{ + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "content": "Write tests", + "completed": false +} +``` + +## `POST /:account_slug/cards/:card_number/steps` + +Creates a new step on a card. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `content` | string | Yes | The step text | +| `completed` | boolean | No | Whether the step is completed (default: `false`) | + +__Request:__ + +```json +{ + "step": { + "content": "Write tests" + } +} +``` + +__Response:__ + +Returns `201 Created` with a `Location` header pointing to the new step. + +## `PUT /:account_slug/cards/:card_number/steps/:step_id` + +Updates a step. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `content` | string | No | The step text | +| `completed` | boolean | No | Whether the step is completed | + +__Request:__ + +```json +{ + "step": { + "completed": true + } +} +``` + +__Response:__ + +Returns the updated step. + +## `DELETE /:account_slug/cards/:card_number/steps/:step_id` + +Deletes a step. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/tags.md b/docs/api/sections/tags.md new file mode 100644 index 000000000..4e1816341 --- /dev/null +++ b/docs/api/sections/tags.md @@ -0,0 +1,26 @@ +# Tags + +Tags are labels that can be applied to cards for organization and filtering. + +## `GET /:account_slug/tags` + +Returns a list of all tags in the account, sorted alphabetically. + +__Response:__ + +```json +[ + { + "id": "03f5v9zo9qlcwwpyc0ascnikz", + "title": "bug", + "created_at": "2025-12-05T19:36:35.534Z", + "url": "http://fizzy.localhost:3006/897362094/cards?tag_ids[]=03f5v9zo9qlcwwpyc0ascnikz" + }, + { + "id": "03f5v9zo9qlcwwpyc0ascnilz", + "title": "feature", + "created_at": "2025-12-05T19:36:35.534Z", + "url": "http://fizzy.localhost:3006/897362094/cards?tag_ids[]=03f5v9zo9qlcwwpyc0ascnilz" + } +] +``` diff --git a/docs/api/sections/users.md b/docs/api/sections/users.md new file mode 100644 index 000000000..1bd939fc0 --- /dev/null +++ b/docs/api/sections/users.md @@ -0,0 +1,99 @@ +# Users + +Users represent people who have access to an account. + +## `GET /:account_slug/users` + +Returns a list of active users in the account. + +__Response:__ + +```json +[ + { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + { + "id": "03f5v9zjysoy0fqs9yg0ei3hq", + "name": "Jason Fried", + "role": "member", + "active": true, + "email_address": "jason@example.com", + "created_at": "2025-12-05T19:36:35.419Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjysoy0fqs9yg0ei3hq" + }, + { + "id": "03f5v9zk1dtqduod5bkhv3k8m", + "name": "Jason Zimdars", + "role": "member", + "active": true, + "email_address": "jz@example.com", + "created_at": "2025-12-05T19:36:35.435Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zk1dtqduod5bkhv3k8m" + }, + { + "id": "03f5v9zk3nw9ja92e7s4h2wbe", + "name": "Kevin Mcconnell", + "role": "member", + "active": true, + "email_address": "kevin@example.com", + "created_at": "2025-12-05T19:36:35.451Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zk3nw9ja92e7s4h2wbe" + } +] +``` + +## `GET /:account_slug/users/:user_id` + +Returns the specified user. + +__Response:__ + +```json +{ + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" +} +``` + +## `PUT /:account_slug/users/:user_id` + +Updates a user. You can only update users you have permission to change. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | No | The user's display name | +| `avatar` | file | No | The user's avatar image | + +__Request:__ + +```json +{ + "user": { + "name": "David H. Hansson" + } +} +``` + +__Response:__ + +Returns `204 No Content` on success. + +## `DELETE /:account_slug/users/:user_id` + +Deactivates a user. You can only deactivate users you have permission to change. + +__Response:__ + +Returns `204 No Content` on success. diff --git a/docs/api/sections/webhooks.md b/docs/api/sections/webhooks.md new file mode 100644 index 000000000..e06a02708 --- /dev/null +++ b/docs/api/sections/webhooks.md @@ -0,0 +1,117 @@ +# Webhooks + +Webhooks notify another application when something happens on a board. Only account admins can list, view, create, update, delete, or reactivate webhooks. + +## `GET /:account_slug/boards/:board_id/webhooks` + +Returns a paginated list of webhooks for a board. + +__Response:__ + +```json +[ + { + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Production API", + "payload_url": "https://api.example.com/webhooks", + "active": true, + "signing_secret": "p94Bx2HjempCdYB4DTyZkY1b", + "subscribed_actions": ["card_published", "card_assigned", "card_closed"], + "created_at": "2025-12-05T19:36:35.534Z", + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy/webhooks/03f5v9zkft4hj9qq0lsn9ohcm", + "board": { + "id": "03f5v9zkft4hj9qq0lsn9ohcy", + "name": "Fizzy", + "all_access": true, + "created_at": "2025-12-05T19:36:35.534Z", + "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + } + } + } +] +``` + +## `GET /:account_slug/boards/:board_id/webhooks/:id` + +Returns a single webhook. + +__Response:__ + +Returns the same webhook shape shown above. + +## `POST /:account_slug/boards/:board_id/webhooks` + +Creates a webhook. + +__Request:__ + +```json +{ + "webhook": { + "name": "Production API", + "url": "https://api.example.com/webhooks", + "subscribed_actions": ["card_published", "card_assigned", "card_closed"] + } +} +``` + +`subscribed_actions` accepts any of: +`card_assigned`, `card_closed`, `card_postponed`, `card_auto_postponed`, `card_board_changed`, `card_published`, `card_reopened`, `card_sent_back_to_triage`, `card_triaged`, `card_unassigned`, `comment_created` + +__Response:__ + +``` +HTTP/1.1 201 Created +Location: http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcy/webhooks/03f5v9zkft4hj9qq0lsn9ohcm.json +``` + +Returns the created webhook in the response body. + +## `PATCH /:account_slug/boards/:board_id/webhooks/:id` + +Updates a webhook. + +__Request:__ + +```json +{ + "webhook": { + "name": "Production API", + "subscribed_actions": ["card_closed"] + } +} +``` + +The `url` is immutable after creation and is ignored on update. + +__Response:__ + +Returns the updated webhook. + +## `DELETE /:account_slug/boards/:board_id/webhooks/:id` + +Deletes a webhook. + +__Response:__ + +Returns `204 No Content` on success. + +## `POST /:account_slug/boards/:board_id/webhooks/:id/activation` + +Reactivates a deactivated webhook. + +__Response:__ + +``` +HTTP/1.1 201 Created +``` + +Returns the reactivated webhook in the response body.