openapi: 3.0.3

info:
  title: Checkvist Open API
  version: "1.0"
  description: |
    Checkvist provides a REST-like API that can be used to integrate Checkvist with
    other tools. Most read operations can return either JSON or XML.

    ## Choosing a format
    Append `.json` or `.xml` to the request URL to pick the representation, e.g.
    `https://checkvist.com/checklists/42.json`. Fields marked **(JSON only)** below
    are present only in JSON responses.

    ## Emulating PUT / DELETE
    If your HTTP client can only send `GET`/`POST`, use `POST` and add a `_method`
    parameter with the desired method name (`PUT`, `DELETE`, ...).

    ## Authentication
    For non-public lists you must authenticate. Either:
    * **HTTP Basic** — username + password, or username + Remote API key
      (from your profile page); or
    * **Token** (preferred) — obtain a token from `/auth/login.json` and pass it as the
      `token` query parameter or the `X-Client-Token` header. The token is valid for
      one day and can be refreshed for 90 days via `/auth/refresh_token.json`.

    ## Timestamp format
    All timestamp fields (`updated_at`, `created_at`) follow these conventions:

    * **XML responses** — [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601),
      e.g. `2009-05-16T11:20:31Z`.
    * **JSON responses** — `YYYY/MM/DD HH:MM:SS +0000` in UTC, e.g.
      `2005/02/01 15:15:10 +0000`. This string is parseable by the JavaScript
      `Date` constructor.

    `Task.updated_at` and `Task.created_at` are JSON-only — they are omitted
    from XML responses. A JSON `updated_at` may be the empty string when the
    item has never been updated.

    Questions? Join the
    [Checkvist OpenAPI forum](https://discuss.checkvist.com/c/checkvist-api/9).

servers:
  - url: https://checkvist.com

security:
  - tokenHeader: []
  - tokenParam: []
  - basicAuth: []

tags:
  - name: Authentication
    description: Obtaining a token and reading the current user.
  - name: Checklists
    description: Creating, reading, updating and deleting lists.
  - name: List items
    description: Working with tasks (list items), their status and tags.
  - name: Notes
    description: Notes (comments) attached to list items.

paths:

  ############################################################
  # Authentication
  ############################################################
  /auth/login.json:
    post:
      tags: [Authentication]
      summary: Obtain an authentication token
      description: |
        Returns a JSON object with a `token` key. Also available as `GET` with the
        same parameters supplied in the query string. Returns `401 Unauthorized`
        when authentication fails, or `422 Unprocessable Entity` when 2-step
        verification is enabled and `token2fa` is missing or wrong.
      security: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                username:
                  type: string
                  description: User login (email).
                remote_key:
                  type: string
                  description: The user's Remote API key (from the profile page) or password.
                token2fa:
                  type: string
                  description: >
                    Required when 2-step verification is enabled: the code from the
                    authentication app. When 2FA is on, `remote_key` must be used (not the password).
                version:
                  type: string
                  description: >
                    API version. Use `2` to get the documented `{ "token": "..." }` response shape.
              required: [username, remote_key]
      responses:
        "200":
          description: Token issued.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Token" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "422": { $ref: "#/components/responses/TwoFactorRequired" }

  /auth/refresh_token.json:
    post:
      tags: [Authentication]
      summary: Refresh an authentication token
      description: |
        Exchanges a previously issued token for a fresh one without re-sending
        credentials. The old token is invalidated. Returns `401 Unauthorized` for a
        bad `old_token`.
      security: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                old_token:
                  type: string
                  description: Previously obtained token.
                version:
                  type: string
                  description: >
                    API version. Use `2` to get the documented `{ "token": "..." }` response shape.
              required: [old_token]
      responses:
        "200":
          description: New token issued.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Token" }
        "401": { $ref: "#/components/responses/Unauthorized" }

  /auth/curr_user.{format}:
    get:
      tags: [Authentication]
      summary: Get the current user
      parameters:
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The currently logged-in user.
          content:
            application/json: { schema: { $ref: "#/components/schemas/AuthUser" } }
            application/xml:  { schema: { $ref: "#/components/schemas/AuthUser" } }

  ############################################################
  # Checklists
  ############################################################
  /checklists.{format}:
    get:
      tags: [Checklists]
      summary: List the user's checklists
      description: |
        Sorted the same way as in the Checkvist UI (most recently updated first by default).
      parameters:
        - $ref: "#/components/parameters/format"
        - name: archived
          in: query
          description: If `true`, returns archived lists.
          schema: { type: boolean }
        - $ref: "#/components/parameters/order"
        - name: skip_stats
          in: query
          description: If `true`, runs faster but omits per-list user/task statistics.
          schema: { type: boolean }
      responses:
        "200":
          description: Array of checklists.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Checklist" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Checklist" } }
    post:
      tags: [Checklists]
      summary: Create a checklist
      parameters:
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "checklist[name]":   { type: string, description: Checklist name. }
                "checklist[public]": { type: string, description: "0 or 1 (optional)." }
              required: ["checklist[name]"]
      responses:
        "200":
          description: The created checklist.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Checklist" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Checklist" } }

  /checklists/{checklist_id}.{format}:
    get:
      tags: [Checklists]
      summary: Get a checklist
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The requested checklist.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Checklist" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Checklist" } }
    put:
      tags: [Checklists]
      summary: Update a checklist
      description: Updates the checklist name and/or public status.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "checklist[name]":   { type: string, description: Checklist name. }
                "checklist[public]": { type: string, description: "0 or 1 (optional)." }
      responses:
        "200":
          description: The updated checklist.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Checklist" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Checklist" } }
    delete:
      tags: [Checklists]
      summary: Delete a checklist
      description: |
        Marks the list (with all its data) for deletion. Actual deletion happens later,
        once a day. Returns the deleted checklist representation.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The deleted checklist.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Checklist" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Checklist" } }

  ############################################################
  # List items (tasks)
  ############################################################
  /checklists/{checklist_id}/tasks.{format}:
    get:
      tags: [List items]
      summary: List items of a checklist
      description: List items are sorted by `parent_id, position`.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/format"
        - $ref: "#/components/parameters/with_notes"
        - $ref: "#/components/parameters/order"
      responses:
        "200":
          description: Array of list items.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
    post:
      tags: [List items]
      summary: Create a list item
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "task[content]":   { type: string, description: Task text. }
                "task[parent_id]": { type: integer, description: ID of the parent task (optional). }
                "task[tags]":      { type: string, description: Comma-separated list of tags to set on the task. }
                "task[due_date]":  { type: string, description: Due date in Checkvist's smart-syntax format. }
                "task[position]":  { type: integer, description: 1-based position (omit to append to the end). }
                "task[priority]":  { type: integer, description: "Optional priority/color, 0..9." }
                "task[status]":    { type: integer, description: "Task status (optional): 0 open, 1 closed, 2 invalidated." }
              required: ["task[content]"]
      responses:
        "200":
          description: The created list item.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Task" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Task" } }

  /checklists/{checklist_id}/tasks/{task_id}.{format}:
    get:
      tags: [List items]
      summary: Get a list item and its parents
      description: |
        Returns the requested item first, followed by its parents up to the root.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
        - $ref: "#/components/parameters/with_notes"
      responses:
        "200":
          description: The item followed by its ancestor items.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
    put:
      tags: [List items]
      summary: Update a list item
      description: Updates the item. This method does not change task status.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
        - $ref: "#/components/parameters/with_notes"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "task[content]":      { type: string, description: New task text. }
                "task[parent_id]":    { type: integer, description: New parent task ID. }
                "task[tags]":         { type: string, description: Comma-separated list of tags to set (replaces the whole set). }
                "task[due_date]":     { type: string, description: Due date in Checkvist's smart-syntax format. }
                "task[position]":     { type: integer, description: 1-based position of the task. }
                "task[priority]":     { type: integer, description: "Optional priority/color, 0..9." }
                "task[assignee_ids]":
                  type: array
                  items: { type: integer }
                  description: >
                    Assignee user IDs (optional). Sent as
                    `task[assignee_ids][]=1&task[assignee_ids][]=2`.
                parse:                { type: boolean, description: "If true, recognise ^due and #tags smart syntax in the updated item." }
      responses:
        "200":
          description: The updated list item.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Task" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Task" } }
    delete:
      tags: [List items]
      summary: Delete a list item
      description: Deletes the item including its children. Returns the deleted item.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The deleted list item.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Task" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Task" } }

  /checklists/{checklist_id}/import.{format}:
    post:
      tags: [List items]
      summary: Import list items
      description: |
        Imports list items (in the format supported by Checkvist's import function),
        organised into a hierarchy according to the input. Items are added to the
        beginning of the target list. Returns the created items.
      parameters:
        - name: checklist_id
          in: path
          required: true
          description: >
            Target list ID. May also be `tag:tagname` — the first list tagged with
            `tagname` is used.
          schema: { type: string }
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                import_content:            { type: string, description: List items to import. }
                import_content_note:       { type: string, description: If present, becomes the note of the first created item. }
                "add_files[1]":            { type: string, description: Optional file attachment data (PRO users). }
                "add_files[2]":            { type: string, description: Optional file attachment data (PRO users). }
                parent_id:                 { type: integer, description: Optional ID of the parent item. }
                parse_tasks:               { type: boolean, description: "If present, recognise smart syntax like ^due, #tags, !3." }
                position:                  { type: integer, description: Optional 1-based position for the first imported item. }
                replace_existing:          { type: boolean, description: "Danger: replace the whole content of the list with imported content." }
                separate_with_empty_line:  { type: string, description: "'singleItem' imports all text as one item; any other value separates items by empty lines; unset treats each line as a separate item." }
                status:                    { type: integer, description: "Optional status for the first imported task: 0, 1 or 2." }
              required: [import_content]
      responses:
        "200":
          description: The created list items.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }

  /checklists/{checklist_id}/tasks/{task_id}/{action}.{format}:
    post:
      tags: [List items]
      summary: Change task status (close / reopen / invalidate)
      description: |
        Changes the status of the item. For non-leaf items, the status of their
        children is changed (the parent status is the cumulative status of its children).
        Returns the changed item and its children.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - name: action
          in: path
          required: true
          description: The status change to apply.
          schema: { type: string, enum: [close, reopen, invalidate] }
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The changed item followed by its children.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }

  /checklists/{checklist_id}/tasks/{task_id}/repeat.{format}:
    post:
      tags: [List items]
      summary: Set repeating-task information
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "repeat[period]":       { type: string, enum: [daily, weekly, monthly, yearly] }
                "repeat[period_count]": { type: integer, description: "e.g. 5 in 'repeat every 5 weeks' (optional, default 1)." }
                "repeat[start_date]":   { type: string, description: Start date for the first repeating due. }
                "repeat[end_date]":     { type: string, description: End date for the repeating due (optional). }
              required: ["repeat[period]", "repeat[start_date]"]
      responses:
        "200":
          description: The updated task.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Task" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Task" } }

  /checklists/{checklist_id}/tasks/{task_id}/tags.{format}:
    post:
      tags: [List items]
      summary: Add or remove tags on a list item
      description: |
        When `delete_tag` is given, that tag is removed from the list item (and from
        every item listed in `task_ids`). Otherwise the item's tags are set from the
        `tags` parameter. Returns the affected list item(s) with their new tags.

        A request **without** a `.json`/`.xml` suffix returns the JavaScript response
        used by the Checkvist web UI instead of a JSON/XML body.
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                delete_tag:
                  type: string
                  description: Name of a single tag to remove from the list item.
                tags:
                  type: string
                  description: Space-separated list of tags to set on the item (used only when `delete_tag` is not given).
                task_ids:
                  type: string
                  description: Optional comma-separated list of extra list item IDs to apply the same change to several items at once.
      responses:
        "200":
          description: The updated list item(s).
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Task" } }

  ############################################################
  # Notes (comments)
  ############################################################
  /checklists/{checklist_id}/tasks/{task_id}/comments.{format}:
    get:
      tags: [Notes]
      summary: Get notes of a list item
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: Array of notes.
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Note" } }
            application/xml:
              schema: { type: array, items: { $ref: "#/components/schemas/Note" } }
    post:
      tags: [Notes]
      summary: Create a note
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "comment[comment]": { type: string, description: Note text. }
              required: ["comment[comment]"]
      responses:
        "200":
          description: The created note.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Note" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Note" } }

  /checklists/{checklist_id}/tasks/{task_id}/comments/{note_id}.{format}:
    put:
      tags: [Notes]
      summary: Update a note
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/note_id"
        - $ref: "#/components/parameters/format"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                "comment[comment]": { type: string, description: Note text. }
              required: ["comment[comment]"]
      responses:
        "200":
          description: The updated note.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Note" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Note" } }
    delete:
      tags: [Notes]
      summary: Delete a note
      parameters:
        - $ref: "#/components/parameters/checklist_id"
        - $ref: "#/components/parameters/task_id"
        - $ref: "#/components/parameters/note_id"
        - $ref: "#/components/parameters/format"
      responses:
        "200":
          description: The deleted note.
          content:
            application/json: { schema: { $ref: "#/components/schemas/Note" } }
            application/xml:  { schema: { $ref: "#/components/schemas/Note" } }

############################################################
# Reusable components
############################################################
components:

  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: Username + password, or username + Remote API key (from the profile page).
    tokenParam:
      type: apiKey
      in: query
      name: token
      description: Token obtained from /auth/login.json.
    tokenHeader:
      type: apiKey
      in: header
      name: X-Client-Token
      description: Token obtained from /auth/login.json.

  parameters:
    format:
      name: format
      in: path
      required: true
      description: Response representation.
      schema:
        type: string
        enum: [json, xml]
    checklist_id:
      name: checklist_id
      in: path
      required: true
      schema: { type: integer }
    task_id:
      name: task_id
      in: path
      required: true
      schema: { type: integer }
    note_id:
      name: note_id
      in: path
      required: true
      schema: { type: integer }
    with_notes:
      name: with_notes
      in: query
      description: If set, the result includes information about notes added to the items.
      schema: { type: boolean }
    order:
      name: order
      in: query
      description: Override the sort order.
      schema:
        type: string
        enum: [id:asc, id:desc, updated_at:asc]

  responses:
    Unauthorized:
      description: Authentication failed.
    TwoFactorRequired:
      description: Two-step verification is enabled and `token2fa` is missing or wrong.
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string }

  schemas:

    Token:
      type: object
      properties:
        token:
          type: string
          description: Authentication token, valid for one day.

    AuthUser:
      type: object
      description: >
        Representation of the currently logged-in user (id, username, email and
        profile/settings data). Field set is not part of the stable contract.
      additionalProperties: true

    Checklist:
      type: object
      properties:
        id:             { type: integer }
        name:           { type: string }
        public:
          type: boolean
          description: True for a checklist that anyone can read without authentication.
        role:
          type: integer
          enum: [1, 2, 3]
          description: "1 - author, 2 - writer, 3 - reader."
        updated_at:
          type: string
          description: >
            Time of the last update (includes task updates, excludes comments).
            See **Timestamp format** in the introduction.
        task_count:     { type: integer, description: Total number of leaf tasks. }
        task_completed: { type: integer, description: Number of completed leaf tasks. }
        read_only:      { type: boolean, description: True if the user's access to this list is read-only. }
        archived:       { type: boolean, description: True if the user archived this list. }
        tags:
          type: object
          additionalProperties: { type: boolean }
          description: "(JSON only) Map of tag name => isPrivate."
        tags_as_text:
          type: string
          description: "(JSON only) Comma-separated list of tag names."

    Task:
      type: object
      description: A list item.
      properties:
        id:             { type: integer }
        content:        { type: string, description: Task content. }
        status:
          type: integer
          enum: [0, 1, 2]
          description: "0 - open, 1 - closed, 2 - invalidated."
        checklist_id:   { type: integer, description: Containing checklist ID. }
        parent_id:
          type: integer
          description: Parent task ID. Empty for root-level items in XML, or 0 in JSON.
        comments_count: { type: integer, description: Number of notes for this item. }
        position:       { type: integer, description: 1-based position under its parent. }
        priority:
          type: integer
          description: "(JSON only) Optional task priority, 1..9."
        due:
          type: string
          description: Due date, when set.
        deleted:
          type: boolean
          description: Present (and `true`) only in the representation of a just-deleted item.
        assignee_ids:
          type: array
          items: { type: integer }
          description: "(JSON only) IDs of the item's assignees."
        tasks:
          type: array
          items: { type: integer }
          description: "(JSON only) Child task IDs."
        tags:
          type: object
          additionalProperties: { type: boolean }
          description: >
            (JSON only) Map of tag name => isPrivate. When modifying an item, both a
            map and a comma-separated string of tag names are accepted.
        tags_as_text:
          type: string
          description: "(JSON only) Comma-separated list of tag names. Also accepted when modifying an item."
        update_line:
          type: string
          description: "(JSON only) Text describing the last change, e.g. 'updated by kir'."
        updated_at:
          type: string
          description: >
            (JSON only) Time of the last change. See **Timestamp format** in
            the introduction.
        created_at:
          type: string
          description: >
            (JSON only) When the item was created. See **Timestamp format** in
            the introduction.
        notes:
          type: array
          items: { $ref: "#/components/schemas/Note" }
          description: Present when `with_notes` is set.

    Note:
      type: object
      description: A note (comment) attached to a list item.
      properties:
        id:         { type: integer }
        comment:    { type: string, description: Note text. }
        task_id:    { type: integer, description: Containing task ID. }
        user_id:    { type: integer, description: ID of the user who added the note. }
        username:   { type: string, description: Username of the user who added the note. }
        updated_at:
          type: string
          description: >
            Time of the last change. See **Timestamp format** in the
            introduction.
        created_at:
          type: string
          description: >
            Time the note was created. See **Timestamp format** in the
            introduction.
