Basecamp API

The Basecamp API is a REST API providing programmatic access to Basecamp's project management platform. Manage projects, to-do lists, messages, documents, schedules, and team members. Uses OAuth 2.0 for authentication and returns JSON.

OpenAPI Specification

basecamp-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Basecamp API
  description: >-
    The Basecamp API is a REST API that provides programmatic access to
    Basecamp's project management and team communication platform. It enables
    developers to manage projects, to-do lists, messages, documents, schedules,
    campfires, uploads, card tables, templates, and team members across Basecamp
    accounts. The API uses OAuth 2.0 for authentication and returns JSON
    responses, with all requests scoped to an account ID in the base URL path.
    Resources include projects, people, to-dos, message boards, documents, card
    tables, campfires, questionnaires, and webhooks, covering the full breadth
    of Basecamp's collaboration toolset.
  version: '1.0'
  contact:
    name: Basecamp Developer Support
    url: https://github.com/basecamp/bc3-api
  termsOfService: https://basecamp.com/terms
externalDocs:
  description: Basecamp API Documentation
  url: https://github.com/basecamp/bc3-api
servers:
  - url: https://3.basecampapi.com/{accountId}
    description: Production Server
    variables:
      accountId:
        description: Your Basecamp account ID
        default: '999999999'
tags:
  - name: Campfires
    description: Manage project campfire chat rooms and lines
  - name: Card Tables
    description: Manage kanban-style card tables and card movements
  - name: Comments
    description: Manage comments on any commentable recording
  - name: Documents
    description: Manage documents stored in vaults
  - name: Messages
    description: Manage messages on message boards
  - name: People
    description: Manage people, profiles, and project access
  - name: Projects
    description: Manage Basecamp projects (buckets)
  - name: Recordings
    description: Common actions for all recordable resources (archive, trash, restore)
  - name: Schedule Entries
    description: Manage individual schedule events and recurring entries
  - name: Schedules
    description: Manage project schedules
  - name: Subscriptions
    description: Manage per-recording notification subscriptions
  - name: Templates
    description: Manage project templates and construct projects from them
  - name: To-Do Lists
    description: Manage to-do lists within a to-do set
  - name: To-Dos
    description: Manage individual to-do items
  - name: Uploads
    description: Manage file uploads stored in vaults
  - name: Webhooks
    description: Manage webhook subscriptions for a project
security:
  - bearerAuth: []
paths:
  /projects.json:
    get:
      operationId: listProjects
      summary: List projects
      description: >-
        Returns a paginated list of active projects visible to the authenticated
        user. Use the optional status parameter to retrieve archived or trashed
        projects instead.
      tags:
        - Projects
      parameters:
        - name: status
          in: query
          description: Filter projects by status. Omit for active projects.
          required: false
          schema:
            type: string
            enum: [archived, trashed]
      responses:
        '200':
          description: Paginated list of projects
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Project'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      operationId: createProject
      summary: Create a project
      description: >-
        Creates a new Basecamp project with the given name and optional
        description. Returns the new project with a 201 Created status.
      tags:
        - Projects
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreateRequest'
      responses:
        '201':
          description: Project created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /projects/{projectId}.json:
    get:
      operationId: getProject
      summary: Get a project
      description: >-
        Returns the project with the given ID including its dock, which lists
        the tools enabled for that project.
      tags:
        - Projects
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateProject
      summary: Update a project
      description: >-
        Updates the name, description, schedule dates, or admissions settings
        for an existing project. Returns the updated project.
      tags:
        - Projects
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdateRequest'
      responses:
        '200':
          description: Updated project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteProject
      summary: Delete a project
      description: >-
        Moves the project with the given ID to trash. Trashed projects can be
        permanently deleted from the Basecamp web interface.
      tags:
        - Projects
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '204':
          description: Project moved to trash
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /people.json:
    get:
      operationId: listPeople
      summary: List all people
      description: >-
        Returns a paginated list of all people visible to the authenticated user
        across the account.
      tags:
        - People
      responses:
        '200':
          description: Paginated list of people
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Person'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /projects/{projectId}/people.json:
    get:
      operationId: listProjectPeople
      summary: List people on a project
      description: >-
        Returns a paginated list of all people with access to the specified
        project.
      tags:
        - People
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: List of people on the project
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Person'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/people/users.json:
    put:
      operationId: updateProjectAccess
      summary: Update project access
      description: >-
        Grants or revokes access to the project for specific people. Accepts
        arrays of person IDs to grant or revoke, or new person objects to
        create and invite.
      tags:
        - People
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectAccessRequest'
      responses:
        '200':
          description: Updated access list
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /circles/people.json:
    get:
      operationId: listPingablePeople
      summary: List pingable people
      description: >-
        Returns a paginated list of all people the authenticated user can ping
        (direct message) within the account.
      tags:
        - People
      responses:
        '200':
          description: List of pingable people
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Person'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /people/{personId}.json:
    get:
      operationId: getPerson
      summary: Get a person
      description: >-
        Returns the profile of the person with the given ID.
      tags:
        - People
      parameters:
        - $ref: '#/components/parameters/PersonId'
      responses:
        '200':
          description: Person profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Person'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /my/profile.json:
    get:
      operationId: getMyProfile
      summary: Get my profile
      description: >-
        Returns the profile of the currently authenticated user.
      tags:
        - People
      responses:
        '200':
          description: Authenticated user profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Person'
        '401':
          $ref: '#/components/responses/Unauthorized'
    put:
      operationId: updateMyProfile
      summary: Update my profile
      description: >-
        Updates profile fields for the currently authenticated user including
        name, email address, title, bio, location, and time zone.
      tags:
        - People
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileUpdateRequest'
      responses:
        '200':
          description: Updated profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Person'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /templates.json:
    get:
      operationId: listTemplates
      summary: List templates
      description: >-
        Returns a paginated list of active project templates visible to the
        authenticated user. Optionally filter by status.
      tags:
        - Templates
      parameters:
        - name: status
          in: query
          description: Filter templates by status.
          required: false
          schema:
            type: string
            enum: [archived, trashed]
      responses:
        '200':
          description: List of templates
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Template'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: createTemplate
      summary: Create a template
      description: >-
        Creates a new project template with the given name and optional
        description. Returns the template with a 201 Created status.
      tags:
        - Templates
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemplateCreateRequest'
      responses:
        '201':
          description: Template created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /templates/{templateId}.json:
    get:
      operationId: getTemplate
      summary: Get a template
      description: >-
        Returns the project template with the given ID.
      tags:
        - Templates
      parameters:
        - $ref: '#/components/parameters/TemplateId'
      responses:
        '200':
          description: Template details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateTemplate
      summary: Update a template
      description: >-
        Updates the name and description of an existing project template.
      tags:
        - Templates
      parameters:
        - $ref: '#/components/parameters/TemplateId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemplateCreateRequest'
      responses:
        '200':
          description: Updated template
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteTemplate
      summary: Delete a template
      description: >-
        Moves the template with the given ID to trash.
      tags:
        - Templates
      parameters:
        - $ref: '#/components/parameters/TemplateId'
      responses:
        '204':
          description: Template trashed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /templates/{templateId}/project_constructions.json:
    post:
      operationId: createProjectFromTemplate
      summary: Create project from template
      description: >-
        Starts the construction of a new project based on the given template.
        Returns a project construction object whose status can be polled until
        it transitions from "pending" to "completed".
      tags:
        - Templates
      parameters:
        - $ref: '#/components/parameters/TemplateId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectConstructionRequest'
      responses:
        '201':
          description: Project construction initiated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectConstruction'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /templates/{templateId}/project_constructions/{constructionId}.json:
    get:
      operationId: getProjectConstruction
      summary: Get project construction status
      description: >-
        Returns the current status of a project construction job. Poll this
        endpoint until status changes from "pending" to "completed".
      tags:
        - Templates
      parameters:
        - $ref: '#/components/parameters/TemplateId'
        - $ref: '#/components/parameters/ConstructionId'
      responses:
        '200':
          description: Project construction status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectConstruction'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/recordings.json:
    get:
      operationId: listRecordings
      summary: List recordings
      description: >-
        Returns a paginated list of recordings of the specified type across all
        projects. Use the type parameter to specify the resource type (Comment,
        Document, Message, Todo, Upload, etc.).
      tags:
        - Recordings
      parameters:
        - name: type
          in: query
          description: The recording type to list.
          required: true
          schema:
            type: string
            enum:
              - Comment
              - Document
              - Message
              - Todo
              - TodoList
              - Schedule::Entry
              - Upload
              - Vault
        - name: bucket
          in: query
          description: Filter to a specific project (bucket) ID.
          required: false
          schema:
            type: integer
        - name: status
          in: query
          description: Filter by recording status.
          required: false
          schema:
            type: string
            enum: [active, archived, trashed]
        - name: sort
          in: query
          description: Sort field.
          required: false
          schema:
            type: string
            enum: [created_at, updated_at]
        - name: direction
          in: query
          description: Sort direction.
          required: false
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: List of recordings
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Recording'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /recordings/{recordingId}/status/trashed.json:
    put:
      operationId: trashRecording
      summary: Trash a recording
      description: >-
        Moves the recording with the given ID to trash. Works for any
        recordable resource type including messages, to-dos, documents, and
        comments.
      tags:
        - Recordings
      parameters:
        - $ref: '#/components/parameters/RecordingId'
      responses:
        '204':
          description: Recording trashed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /recordings/{recordingId}/status/archived.json:
    put:
      operationId: archiveRecording
      summary: Archive a recording
      description: >-
        Archives the recording with the given ID. Archived recordings are
        hidden from default views but remain accessible.
      tags:
        - Recordings
      parameters:
        - $ref: '#/components/parameters/RecordingId'
      responses:
        '204':
          description: Recording archived
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /recordings/{recordingId}/status/active.json:
    put:
      operationId: unarchiveRecording
      summary: Unarchive a recording
      description: >-
        Restores an archived recording to active status, making it visible in
        default views again.
      tags:
        - Recordings
      parameters:
        - $ref: '#/components/parameters/RecordingId'
      responses:
        '204':
          description: Recording restored to active
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /message_boards/{messageBoardId}/messages.json:
    get:
      operationId: listMessages
      summary: List messages
      description: >-
        Returns a paginated list of active messages in the specified message
        board. Messages can be sorted by created_at or updated_at.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/MessageBoardId'
        - name: sort
          in: query
          description: Sort field.
          required: false
          schema:
            type: string
            enum: [created_at, updated_at]
        - name: direction
          in: query
          description: Sort direction.
          required: false
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: List of messages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Message'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      operationId: createMessage
      summary: Create a message
      description: >-
        Creates a new message in the specified message board. Set status to
        "active" to publish immediately; otherwise the message is saved as a
        draft.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/MessageBoardId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageCreateRequest'
      responses:
        '201':
          description: Message created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /messages/{messageId}.json:
    get:
      operationId: getMessage
      summary: Get a message
      description: >-
        Returns the message with the given ID.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/MessageId'
      responses:
        '200':
          description: Message details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateMessage
      summary: Update a message
      description: >-
        Updates the subject, content, or category of the specified message.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/MessageId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageUpdateRequest'
      responses:
        '200':
          description: Updated message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /recordings/{recordingId}/comments.json:
    get:
      operationId: listComments
      summary: List comments
      description: >-
        Returns a paginated list of active comments on the specified recording.
        Any resource that exposes a comments_url can have its comments listed
        via this endpoint.
      tags:
        - Comments
      parameters:
        - $ref: '#/components/parameters/RecordingId'
      responses:
        '200':
          description: List of comments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Comment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      operationId: createComment
      summary: Create a comment
      description: >-
        Creates a new comment on the specified recording. All subscribers to
        the recording will be notified. Supports HTML-formatted content.
      tags:
        - Comments
      parameters:
        - $ref: '#/components/parameters/RecordingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CommentCreateRequest'
      responses:
        '201':
          description: Comment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Comment'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /comments/{commentId}.json:
    get:
      operationId: getComment
      summary: Get a comment
      description: >-
        Returns the comment with the given ID.
      tags:
        - Comments
      parameters:
        - $ref: '#/components/parameters/CommentId'
      responses:
        '200':
          description: Comment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Comment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateComment
      summary: Update a comment
      description: >-
        Updates the content of the specified comment.
      tags:
        - Comments
      parameters:
        - $ref: '#/components/parameters/CommentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CommentCreateRequest'
      responses:
        '200':
          description: Updated comment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Comment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /todosets/{todosetId}/todolists.json:
    get:
      operationId: listTodoLists
      summary: List to-do lists
      description: >-
        Returns a paginated list of active to-do lists within the specified
        to-do set.
      tags:
        - To-Do Lists
      parameters:
        - $ref: '#/components/parameters/TodosetId'
        - name: status
          in: query
          required: false
          description: Filter by status.
          schema:
            type: string
            enum: [archived, trashed]
      responses:
        '200':
          description: List of to-do lists
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TodoList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      operationId: createTodoList
      summary: Create a to-do list
      description: >-
        Creates a new to-do list in the specified to-do set with the given name
        and optional description.
      tags:
        - To-Do Lists
      parameters:
        - $ref: '#/components/parameters/TodosetId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoListCreateRequest'
      responses:
        '201':
          description: To-do list created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /todolists/{todolistId}.json:
    get:
      operationId: getTodoList
      summary: Get a to-do list
      description: >-
        Returns the to-do list with the given ID.
      tags:
        - To-Do Lists
      parameters:
        - $ref: '#/components/parameters/TodolistId'
      responses:
        '200':
          description: To-do list details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateTodoList
      summary: Update a to-do list
      description: >-
        Updates the name and description of the specified to-do list.
      tags:
        - To-Do Lists
      parameters:
        - $ref: '#/components/parameters/TodolistId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoListCreateRequest'
      responses:
        '200':
          description: Updated to-do list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /todolists/{todolistId}/todos.json:
    get:
      operationId: listTodos
      summary: List to-dos
      description: >-
        Returns a paginated list of active to-dos in the specified to-do list.
        Set completed=true to retrieve completed items instead.
      tags:
        - To-Dos
      parameters:
        - $ref: '#/components/parameters/TodolistId'
        - name: status
          in: query
          required: false
          description: Filter by status.
          schema:
            type: string
            enum: [archived, trashed]
        - name: completed
          in: query
          required: false
          description: Set to true to return completed to-dos.
          schema:
            type: boolean
      responses:
        '200':
          description: List of to-dos
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Todo'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      operationId: createTodo
      summary: Create a to-do
      description: >-
        Creates a new to-do in the specified to-do list. Supports assignees,
        due dates, start dates, and completion subscriber notifications.
      tags:
        - To-Dos
      parameters:
        - $ref: '#/components/parameters/TodolistId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoCreateRequest'
      responses:
        '201':
          description: To-do created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /todos/{todoId}.json:
    get:
      operationId: getTodo
      summary: Get a to-do
      description: >-
        Returns the to-do with the given ID.
      tags:
        - To-Dos
      parameters:
        - $ref: '#/components/parameters/TodoId'
      responses:
        '200':
          description: To-do details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateTodo
      summary: Update a to-do
      description: >-
        Updates the content, description, assignees, due date, or other fields
        of the specified to-do.
      tags:
        - To-Dos
      parameters:
        - $ref: '#/components/parameters/TodoId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoCreateRequest'
      responses:
        '200':
          description: Updated to-do
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFo

# --- truncated at 32 KB (90 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/basecamp/refs/heads/main/openapi/basecamp-api-openapi.yml