{
  "openapi": "3.1.0",
  "info": {
    "title": "Gantt400 API",
    "version": "1",
    "description": "Projects, tasks, dependencies, board and API keys of the Gantt400 API. Every operation states how to authenticate: with an API key (`apiKey`, listing the scopes every request needs) or only with the web app session (`session`). More routes will follow step by step. Generated from the schemas the server validates requests with. The server is authoritative: it counts length limits (`maxLength`) in UTF-16 code units, so a character outside the BMP (e.g. an emoji) counts twice. Getting started: https://gantt400.com/en/developers/"
  },
  "servers": [
    {
      "url": "https://app.gantt400.com"
    }
  ],
  "tags": [
    {
      "name": "Projects"
    },
    {
      "name": "Tasks"
    },
    {
      "name": "Dependencies"
    },
    {
      "name": "Board"
    },
    {
      "name": "API keys"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key `g4k_…`, created under Settings → API keys (https://gantt400.com/en/developers/). The list on an operation names the scopes that EVERY request to it needs; what a request additionally needs depending on its content (e.g. dates, anchors, `column_id`) is named in the description of the operation. If a scope is missing, the server answers `403` — at the route level with `forbidden_api_key_scope`, otherwise with the operation's own code (`schedule_requires_read_scope`, `scheduled_requires_task_scope`, `anchors_require_board_read`, `task_tree_requires_scopes` with `missing_scopes`, …)."
      },
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-gantt400_session",
        "description": "The web app session (sign-in via `/api/auth/login`). Write requests also need the app's `Origin` header (`403 csrf_origin_mismatch`), and with a body also `Content-Type: application/json` (`415 csrf_content_type_required`)."
      }
    },
    "schemas": {
      "ProjectListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "source_key": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "integer"
          },
          "task_count": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "source_key",
          "updated_at",
          "task_count"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "ref": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "missing_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "field": {
            "type": "string"
          },
          "task_revision": {
            "type": "integer"
          },
          "task_id": {
            "type": "integer"
          },
          "project_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "reason": {
            "type": "string"
          },
          "scope": {
            "type": "string"
          },
          "blockers": {
            "type": "array",
            "items": {}
          }
        },
        "required": [
          "error"
        ],
        "description": "`error` is the stable code. More fields may be added."
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "integer"
          },
          "updated_at": {
            "type": "integer"
          },
          "board_enabled": {
            "type": "boolean"
          },
          "progress_sync_enabled": {
            "type": "boolean"
          },
          "source_key": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "user_id",
          "name",
          "created_at",
          "updated_at",
          "board_enabled",
          "progress_sync_enabled",
          "source_key"
        ],
        "additionalProperties": false
      },
      "InverseAction": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          }
        },
        "required": [
          "kind"
        ],
        "description": "The inverse action for the web app's undo. Only `kind` is stable; an API client does not need it."
      },
      "ProjectCreated": {
        "type": "object",
        "properties": {
          "project": {
            "$ref": "#/components/schemas/Project"
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "project",
          "inverse_action"
        ],
        "additionalProperties": false
      },
      "ProjectCreateBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "x-gantt400-max-utf16-units": 200
          },
          "source_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "not": {
              "enum": [
                "demo-produktlaunch"
              ]
            },
            "x-gantt400-max-utf16-units": 200
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "description": "`source_key` is the idempotency key per team (e.g. `agentic:<repo>`); a key already in use is `409 source_key_conflict`."
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "is_milestone": {
            "type": "boolean"
          },
          "is_meta": {
            "type": "boolean"
          },
          "meta_dormant": {
            "type": "boolean"
          },
          "progress": {
            "type": "integer"
          },
          "pin_date": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "assignee_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "integer"
          },
          "updated_at": {
            "type": "integer"
          },
          "task_revision": {
            "type": "integer"
          },
          "in_schedule": {
            "type": "boolean"
          },
          "column_id": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "project_id",
          "name",
          "start_date",
          "end_date",
          "parent_id",
          "is_milestone",
          "is_meta",
          "meta_dormant",
          "progress",
          "pin_date",
          "sort_order",
          "assignee_user_id",
          "created_at",
          "updated_at",
          "task_revision",
          "in_schedule"
        ],
        "additionalProperties": false,
        "description": "`column_id` is absent for an API key, and `null` for a task without a card or when the board is disabled. `task_revision` is the token for `if_match_revision`."
      },
      "Dependency": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          },
          "predecessor_id": {
            "type": "integer"
          },
          "successor_id": {
            "type": "integer"
          },
          "lag_days": {
            "type": "integer"
          },
          "link_type": {
            "type": "string",
            "enum": [
              "FS",
              "SS",
              "FF",
              "SF"
            ]
          },
          "created_at": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "project_id",
          "predecessor_id",
          "successor_id",
          "lag_days",
          "link_type",
          "created_at"
        ],
        "additionalProperties": false
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "task_id": {
            "type": "integer"
          },
          "body": {
            "type": "string"
          },
          "created_at": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "task_id",
          "body",
          "created_at"
        ],
        "additionalProperties": false
      },
      "BoardColumn": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "backlog",
              "in_progress",
              "review",
              "done",
              "custom",
              "archive"
            ]
          },
          "position": {
            "type": "string"
          },
          "is_terminal": {
            "type": "boolean"
          },
          "hide_in_gantt": {
            "type": "boolean"
          },
          "schedule_membership": {
            "type": "string",
            "enum": [
              "visible",
              "hidden",
              "detached"
            ]
          },
          "is_gantt_intake": {
            "type": "boolean"
          },
          "created_at": {
            "type": "integer"
          },
          "updated_at": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "project_id",
          "name",
          "kind",
          "position",
          "is_terminal",
          "hide_in_gantt",
          "schedule_membership",
          "is_gantt_intake",
          "created_at",
          "updated_at"
        ],
        "additionalProperties": false,
        "description": "`schedule_membership`: `visible` — the cards appear in the Gantt chart; `hidden` — they count, but without a row; `detached` — they are outside the schedule. `hide_in_gantt` is deprecated."
      },
      "ProjectPrefs": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "project_id": {
            "type": "integer"
          },
          "view_mode": {
            "type": "string",
            "enum": [
              "gantt",
              "board"
            ]
          },
          "zoom_preset": {
            "type": "string",
            "enum": [
              "week",
              "month",
              "quarter",
              "year",
              "range"
            ]
          },
          "viewport_start": {
            "type": [
              "string",
              "null"
            ]
          },
          "viewport_end": {
            "type": [
              "string",
              "null"
            ]
          },
          "viewport_span_days": {
            "type": [
              "number",
              "null"
            ]
          },
          "range_task_a_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "range_task_b_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_preset_before_range": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "week",
              "month",
              "quarter",
              "year",
              null
            ]
          },
          "sidebar_width_px": {
            "type": "integer"
          },
          "vertical_scroll_offset_px": {
            "type": "integer"
          },
          "drawer_pinned": {
            "type": "boolean"
          },
          "critical_path_visible": {
            "type": "boolean"
          },
          "collapsed_task_ids": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "collapsed_board_column_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "reveal_hidden": {
            "type": "boolean"
          },
          "updated_at": {
            "type": "integer"
          }
        },
        "required": [
          "project_id",
          "view_mode",
          "zoom_preset",
          "viewport_start",
          "viewport_end",
          "viewport_span_days",
          "range_task_a_id",
          "range_task_b_id",
          "last_preset_before_range",
          "sidebar_width_px",
          "vertical_scroll_offset_px",
          "drawer_pinned",
          "critical_path_visible",
          "collapsed_task_ids",
          "collapsed_board_column_ids",
          "reveal_hidden",
          "updated_at"
        ],
        "additionalProperties": false
      },
      "ProjectDetail": {
        "type": "object",
        "properties": {
          "project": {
            "$ref": "#/components/schemas/Project"
          },
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            }
          },
          "offschedule_tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            }
          },
          "deps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Dependency"
            }
          },
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            }
          },
          "board_columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardColumn"
            }
          },
          "can_mutate": {
            "type": "boolean"
          },
          "prefs": {
            "$ref": "#/components/schemas/ProjectPrefs"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "project",
          "tasks",
          "offschedule_tasks",
          "deps",
          "comments",
          "board_columns",
          "can_mutate",
          "prefs",
          "revision"
        ],
        "additionalProperties": false
      },
      "ProjectUpdated": {
        "type": "object",
        "properties": {
          "project": {
            "$ref": "#/components/schemas/Project"
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "project",
          "revision"
        ],
        "additionalProperties": false
      },
      "ProjectEditBody": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "edit"
                ]
              },
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "x-gantt400-max-utf16-units": 200
              },
              "expected_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "x-gantt400-max-utf16-units": 200
              }
            },
            "required": [
              "mode",
              "name"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "undo"
                ]
              },
              "restore_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "x-gantt400-max-utf16-units": 200
              },
              "expected_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "x-gantt400-max-utf16-units": 200
              }
            },
            "required": [
              "mode",
              "restore_name",
              "expected_name"
            ],
            "additionalProperties": false
          }
        ],
        "description": "`mode: edit` renames; with `expected_name` only if the name is still that value (otherwise `409 name_conflict`). `mode: undo` is the web app's undo."
      },
      "TaskSlice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "is_milestone": {
            "type": "boolean"
          },
          "is_meta": {
            "type": "boolean"
          },
          "meta_dormant": {
            "type": "boolean"
          },
          "progress": {
            "type": "integer"
          },
          "pin_date": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "assignee_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "integer"
          },
          "updated_at": {
            "type": "integer"
          },
          "task_revision": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "project_id",
          "name",
          "start_date",
          "end_date",
          "parent_id",
          "is_milestone",
          "is_meta",
          "meta_dormant",
          "progress",
          "pin_date",
          "sort_order",
          "assignee_user_id",
          "created_at",
          "updated_at",
          "task_revision"
        ],
        "additionalProperties": false,
        "description": "A task without its board and schedule membership (`in_schedule`, `column_id`)."
      },
      "TaskList": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskSlice"
            }
          },
          "deps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Dependency"
            }
          }
        },
        "required": [
          "tasks",
          "deps"
        ],
        "additionalProperties": false
      },
      "TaskCreated": {
        "type": "object",
        "properties": {
          "task": {
            "$ref": "#/components/schemas/Task"
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "task",
          "inverse_action"
        ],
        "additionalProperties": false
      },
      "TaskCreateBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "x-gantt400-max-utf16-units": 500
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "is_milestone": {
            "type": "boolean"
          },
          "is_meta": {
            "type": "boolean"
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "pin_date": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer",
            "minimum": -2147483648,
            "maximum": 2147483647
          }
        },
        "required": [
          "name",
          "start_date",
          "end_date"
        ],
        "additionalProperties": false,
        "description": "End ≥ start (YYYY-MM-DD); a milestone has start = end and a progress of 0 or 100. `is_meta` (a project container, session only) excludes `is_milestone` and sits at the top level (`parent_id` empty, otherwise `400 task_parent_invariant`).",
        "allOf": [
          {
            "not": {
              "properties": {
                "is_meta": {
                  "const": true
                },
                "is_milestone": {
                  "const": true
                }
              },
              "required": [
                "is_meta",
                "is_milestone"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "is_meta": {
                  "const": true
                }
              },
              "required": [
                "is_meta"
              ]
            },
            "then": {
              "properties": {
                "parent_id": {
                  "type": "null"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "is_milestone": {
                  "const": true
                }
              },
              "required": [
                "is_milestone"
              ]
            },
            "then": {
              "properties": {
                "progress": {
                  "enum": [
                    0,
                    100
                  ]
                }
              }
            }
          }
        ]
      },
      "TaskUpdated": {
        "type": "object",
        "properties": {
          "task": {
            "$ref": "#/components/schemas/Task"
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "task"
        ],
        "additionalProperties": false
      },
      "TaskPatchBody": {
        "type": "object",
        "properties": {
          "if_match_revision": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "x-gantt400-max-utf16-units": 500
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "is_milestone": {
            "type": "boolean"
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "pin_date": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer",
            "minimum": -2147483648,
            "maximum": 2147483647
          },
          "assignee_user_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "in_schedule": {
            "type": "boolean",
            "const": true
          }
        },
        "additionalProperties": false,
        "description": "At least one field besides `if_match_revision`. Dates are changed by `PATCH /api/tasks/{id}/reschedule`. `if_match_revision` is the `task_revision` the change is based on (otherwise `409 task_revision_conflict`). `in_schedule: false` is rejected.",
        "allOf": [
          {
            "if": {
              "properties": {
                "is_milestone": {
                  "const": true
                }
              },
              "required": [
                "is_milestone"
              ]
            },
            "then": {
              "properties": {
                "progress": {
                  "enum": [
                    0,
                    100
                  ]
                }
              }
            }
          }
        ],
        "anyOf": [
          {
            "required": [
              "name"
            ]
          },
          {
            "required": [
              "parent_id"
            ]
          },
          {
            "required": [
              "is_milestone"
            ]
          },
          {
            "required": [
              "progress"
            ]
          },
          {
            "required": [
              "pin_date"
            ]
          },
          {
            "required": [
              "sort_order"
            ]
          },
          {
            "required": [
              "assignee_user_id"
            ]
          },
          {
            "required": [
              "in_schedule"
            ]
          }
        ]
      },
      "TaskRescheduled": {
        "type": "object",
        "properties": {
          "task": {
            "$ref": "#/components/schemas/Task"
          },
          "cascaded_tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            }
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "task",
          "cascaded_tasks",
          "inverse_action"
        ],
        "additionalProperties": false
      },
      "TaskRescheduleBody": {
        "type": "object",
        "properties": {
          "new_start_date": {
            "type": "string",
            "format": "date"
          },
          "new_end_date": {
            "type": "string",
            "format": "date"
          },
          "if_match_revision": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "new_start_date",
          "new_end_date"
        ],
        "additionalProperties": false,
        "description": "End ≥ start. An API key MUST send `if_match_revision` (`400 invalid_payload`, `code: if_match_revision_required`)."
      },
      "TasksMoved": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            }
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "tasks"
        ],
        "additionalProperties": false
      },
      "TaskMoveBody": {
        "type": "object",
        "properties": {
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "after_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "parent_id",
          "after_id"
        ],
        "additionalProperties": false,
        "description": "Under `parent_id` (`null` = top level), directly after `after_id` (`null` = first position). The server renumbers the siblings."
      },
      "TaskDeleted": {
        "type": "object",
        "properties": {
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          }
        },
        "required": [
          "inverse_action",
          "project_id"
        ],
        "additionalProperties": false
      },
      "DependencyCreated": {
        "type": "object",
        "properties": {
          "dep": {
            "$ref": "#/components/schemas/Dependency"
          },
          "inverse_action": {
            "$ref": "#/components/schemas/InverseAction"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "dep",
          "inverse_action"
        ],
        "additionalProperties": false
      },
      "DependencyCreateBody": {
        "type": "object",
        "properties": {
          "predecessor_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "successor_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "lag_days": {
            "type": "integer",
            "minimum": -3650,
            "maximum": 3650
          },
          "link_type": {
            "type": "string",
            "enum": [
              "FS",
              "SS",
              "FF",
              "SF"
            ]
          }
        },
        "required": [
          "predecessor_id",
          "successor_id"
        ],
        "additionalProperties": false,
        "description": "`lag_days` (default 0) and `link_type` (default `FS`) are optional. An invalid edge is `400 dep_invariant` with `code` (e.g. `cycle`, `duplicate`, `self_loop`, `endpoint_unscheduled`)."
      },
      "BoardItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "project_id": {
            "type": "integer"
          },
          "column_id": {
            "type": "integer"
          },
          "position": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignee_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "assignee_status": {
            "type": "string",
            "enum": [
              "none",
              "active",
              "inactive",
              "ineligible"
            ]
          },
          "created_at": {
            "type": "integer"
          },
          "updated_at": {
            "type": "integer"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "meta_id": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "project_id",
          "column_id",
          "position",
          "title",
          "description",
          "assignee_user_id",
          "assignee_status",
          "created_at",
          "updated_at"
        ],
        "additionalProperties": false
      },
      "BoardDependency": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "predecessor_id": {
            "type": "integer"
          },
          "predecessor_title": {
            "type": "string"
          },
          "successor_id": {
            "type": "integer"
          },
          "successor_title": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "predecessor_id",
          "predecessor_title",
          "successor_id",
          "successor_title"
        ],
        "additionalProperties": false
      },
      "BoardMeta": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "dormant": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "title",
          "dormant"
        ],
        "additionalProperties": false
      },
      "BoardSnapshot": {
        "type": "object",
        "properties": {
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardColumn"
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardItem"
            }
          },
          "deps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardDependency"
            }
          },
          "metas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardMeta"
            }
          }
        },
        "required": [
          "columns",
          "items"
        ],
        "additionalProperties": false,
        "description": "`deps` and `metas` (and the cards' `parent_id`/`meta_id`) are sent only to the session; for an API key they are absent."
      },
      "BoardSync": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "disabled",
              "non_boundary",
              "skipped_milestone",
              "skipped_external_ref",
              "progress_set_zero",
              "progress_set_hundred",
              "progress_noop"
            ]
          },
          "task_id": {
            "type": "integer"
          },
          "progress_value": {
            "anyOf": [
              {
                "type": "number",
                "enum": [
                  0
                ]
              },
              {
                "type": "number",
                "enum": [
                  100
                ]
              }
            ]
          }
        },
        "required": [
          "status"
        ],
        "additionalProperties": false,
        "description": "Whether the move set the task's progress: 0 when moved into the backlog column, 100 when moved into the done column."
      },
      "BoardItemMoved": {
        "type": "object",
        "properties": {
          "item": {
            "$ref": "#/components/schemas/BoardItem"
          },
          "task": {
            "$ref": "#/components/schemas/Task"
          },
          "sync": {
            "$ref": "#/components/schemas/BoardSync"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "item",
          "sync"
        ],
        "additionalProperties": false
      },
      "BoardItemMoveBody": {
        "type": "object",
        "properties": {
          "column_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "before_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "after_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "if_match_updated_at": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        },
        "required": [
          "column_id"
        ],
        "description": "Target column, optionally directly before `before_id` or after `after_id` (mutually exclusive; a key needs `board:read` for them). `if_match_updated_at` is the card's `updated_at` the move is based on (otherwise `409 concurrent_modification`).",
        "not": {
          "required": [
            "before_id",
            "after_id"
          ]
        }
      },
      "BoardItemCreated": {
        "type": "object",
        "properties": {
          "item": {
            "$ref": "#/components/schemas/BoardItem"
          },
          "task": {
            "$ref": "#/components/schemas/Task"
          },
          "revision": {
            "type": "integer"
          }
        },
        "required": [
          "item"
        ],
        "additionalProperties": false
      },
      "BoardItemCreateBody": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "x-gantt400-max-utf16-units": 500
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20000,
            "x-gantt400-max-utf16-units": 20000
          },
          "column_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "assignee_user_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "before_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "after_id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "is_milestone": {
            "type": "boolean"
          },
          "expect": {
            "type": "object",
            "properties": {
              "project_source_key": {
                "type": [
                  "string",
                  "null"
                ],
                "minLength": 1,
                "maxLength": 200,
                "x-gantt400-max-utf16-units": 200
              },
              "column_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120,
                "x-gantt400-max-utf16-units": 120
              },
              "column_schedule_membership": {
                "type": "string",
                "enum": [
                  "visible",
                  "hidden",
                  "detached"
                ]
              }
            },
            "required": [
              "project_source_key",
              "column_name",
              "column_schedule_membership"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "title",
          "column_id"
        ],
        "additionalProperties": false,
        "description": "`start_date` and `end_date` come only together (YYYY-MM-DD, end ≥ start); a milestone has start = end; `before_id` and `after_id` are mutually exclusive.",
        "dependentRequired": {
          "start_date": [
            "end_date"
          ],
          "end_date": [
            "start_date"
          ]
        },
        "not": {
          "required": [
            "before_id",
            "after_id"
          ]
        }
      },
      "TaskTreeNodeResult": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string"
          },
          "task_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "created",
              "updated",
              "unchanged"
            ]
          },
          "changed": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "column_ignored": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        },
        "required": [
          "ref",
          "task_id",
          "action",
          "changed"
        ],
        "additionalProperties": false
      },
      "TaskTreeResult": {
        "type": "object",
        "properties": {
          "dry_run": {
            "type": "boolean"
          },
          "nodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskTreeNodeResult"
            }
          },
          "created": {
            "type": "integer"
          },
          "updated": {
            "type": "integer"
          },
          "unchanged": {
            "type": "integer"
          },
          "cascaded_task_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "revision": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "dry_run",
          "nodes",
          "created",
          "updated",
          "unchanged",
          "cascaded_task_ids"
        ],
        "additionalProperties": false
      },
      "TaskTreeNode": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]*$",
            "x-gantt400-max-utf16-units": 128
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "x-gantt400-max-utf16-units": 500
          },
          "start_date": {
            "type": "string",
            "format": "date"
          },
          "end_date": {
            "type": "string",
            "format": "date"
          },
          "is_milestone": {
            "type": "boolean"
          },
          "column_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "children": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/TaskTreeNode"
            }
          }
        },
        "required": [
          "ref",
          "name"
        ],
        "additionalProperties": false,
        "description": "`start_date` and `end_date` come only together (YYYY-MM-DD, end ≥ start); a milestone has start = end and no children. `ref` is the idempotency key per project.",
        "dependentRequired": {
          "start_date": [
            "end_date"
          ],
          "end_date": [
            "start_date"
          ]
        },
        "if": {
          "properties": {
            "is_milestone": {
              "const": true
            }
          },
          "required": [
            "is_milestone"
          ]
        },
        "then": {
          "not": {
            "required": [
              "children"
            ],
            "properties": {
              "children": {
                "minItems": 1
              }
            }
          }
        }
      },
      "TaskTreeBody": {
        "type": "object",
        "properties": {
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "nodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskTreeNode"
            },
            "minItems": 1,
            "maxItems": 500
          }
        },
        "required": [
          "nodes"
        ],
        "additionalProperties": false
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_by_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "integer"
          },
          "last_used_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "expires_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "revoked_at": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "key_prefix",
          "role",
          "scopes",
          "created_by_user_id",
          "created_at",
          "last_used_at",
          "expires_at",
          "revoked_at"
        ],
        "additionalProperties": false
      },
      "ApiKeyCreated": {
        "type": "object",
        "properties": {
          "api_key": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "secret": {
            "type": "string",
            "description": "The key `g4k_…`. Only in this response — the server does not store it."
          }
        },
        "required": [
          "api_key",
          "secret"
        ],
        "additionalProperties": false
      },
      "ApiKeyCreateBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "x-gantt400-max-utf16-units": 120
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 90
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "projects:read",
                "projects:create",
                "board:read",
                "board_items:create",
                "board_items:update",
                "board_items:delete",
                "board_columns:create",
                "tasks:read",
                "tasks:create",
                "tasks:update",
                "tasks:delete"
              ]
            },
            "minItems": 1,
            "maxItems": 11
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "description": "`scopes` from: `projects:read`, `projects:create`, `board:read`, `board_items:create`, `board_items:update`, `board_items:delete`, `board_columns:create`, `tasks:read`, `tasks:create`, `tasks:update`, `tasks:delete`; if omitted, only `board_items:create`. An unknown scope is `400 invalid_scope`. `expires_in_days` 1–365, default 90."
      },
      "ApiKeyList": {
        "type": "object",
        "properties": {
          "api_keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          }
        },
        "required": [
          "api_keys"
        ],
        "additionalProperties": false
      }
    },
    "parameters": {}
  },
  "paths": {
    "/api/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List the team's projects",
        "description": "The team's projects that are not deleted, most recently edited first. For a key, the way to find a project again by its `source_key`.",
        "security": [
          {
            "apiKey": [
              "projects:read"
            ]
          },
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "The projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProjectListItem"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated (`unauthorized`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope missing (`forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createProject",
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "description": "Requires at least the `editor` role, not restricted to individual projects. The new project gets the board's default columns. `409 source_key_conflict` names the existing project in `project_id` only if the caller may find it anyway (session, or a key with `projects:read`), otherwise `null`.",
        "security": [
          {
            "apiKey": [
              "projects:create"
            ]
          },
          {
            "session": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`invalid_payload`) or reserved key (`reserved_source_key`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing (`forbidden_insufficient_role`, `forbidden_project_scoped`, `forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`source_key` already taken (`source_key_conflict`, with `project_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Projects"
        ],
        "summary": "Project with tasks, dependencies, comments and view settings",
        "description": "Session only: the response carries comments and the member's view settings, which no scope covers. A key reads tasks and dependencies via `GET /api/projects/{id}/tasks`.",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (`invalid_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Requested with an API key (`forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found or deleted (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateProject",
        "tags": [
          "Projects"
        ],
        "summary": "Rename a project",
        "description": "Session only, requires at least the `editor` role. `source_key` cannot be changed (`400 invalid_payload`).",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectEditBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed, or unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectUpdated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or invalid body (`invalid_id`, `invalid_payload`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role missing or read-only access (`forbidden_insufficient_role`, `project_read_only`, `forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Name changed in the meantime (`name_conflict`) or concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted (`project_gone`) or undo expired (`stale_undo`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}/tasks": {
      "get": {
        "operationId": "listTasks",
        "tags": [
          "Tasks",
          "Dependencies"
        ],
        "summary": "Read a project's tasks and dependencies",
        "description": "The tasks in the schedule (without cards outside the schedule) and the dependencies between them. Without comments and view settings.",
        "security": [
          {
            "apiKey": [
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Tasks and dependencies",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (`invalid_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope missing (`forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found or deleted (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Create a task",
        "description": "Requires at least the `editor` role. Not idempotent — for repeatable creation use `POST /api/projects/{id}/task-tree`.",
        "security": [
          {
            "apiKey": [
              "tasks:create"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, invalid body or invalid parent (`invalid_id`, `invalid_payload`, `task_parent_invariant`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `project_read_only`); `meta_session_only`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Parent deleted (`parent_gone`) or concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted (`project_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/tasks/{id}": {
      "patch": {
        "operationId": "updateTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Change a task's fields",
        "description": "Requires at least the `editor` role.",
        "security": [
          {
            "apiKey": [
              "tasks:update"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskPatchBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Changed, or unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskUpdated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, invalid body or violated rule (`invalid_id`, `invalid_payload`, `milestone_requires_single_date`, `milestone_progress_not_binary`, `milestone_has_children`, `schedule_membership`, `task_parent_invariant`, `board_invariant` with `code` for an invalid assignment)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale `if_match_revision` (`task_revision_conflict`, with `task_revision`), parent deleted (`parent_gone`) or concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Task deleted (`task_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Delete a task with its subtasks",
        "description": "Requires at least the `editor` role. Deletes the subtree of tasks that are not deleted yet; the web app can restore it for 15 minutes. `expect_subtree=self` deletes only if the task has no subtasks that are not deleted (otherwise `409 subtree_grew`). Further query parameters belong to the web app's undo and are not part of this API.",
        "security": [
          {
            "apiKey": [
              "tasks:delete"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "self"
              ]
            },
            "required": false,
            "name": "expect_subtree",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskDeleted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or query (`invalid_id`, `invalid_query`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Project container with children (`meta_has_children`) or subtree grew (`subtree_grew`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project or task already deleted (`project_gone`, `task_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/tasks/{id}/reschedule": {
      "patch": {
        "operationId": "rescheduleTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Set a task's dates and move its successors along",
        "description": "Requires at least the `editor` role. The cascade moves successors along the dependencies; the response lists them in `cascaded_tasks`. A key can only address tasks that `GET /api/projects/{id}/tasks` shows it — anything else is `404`.",
        "security": [
          {
            "apiKey": [
              "tasks:update",
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskRescheduleBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rescheduled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRescheduled"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or invalid body (`invalid_id`, `invalid_payload`, also with `code: if_match_revision_required`), task not in the schedule (`schedule_membership`) or violated rule (`reschedule_invariant`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `schedule_requires_read_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale `if_match_revision` (`task_revision_conflict`, with `task_revision`) or concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Task deleted (`task_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/tasks/{id}/move": {
      "post": {
        "operationId": "moveTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Reparent or reorder a task",
        "description": "Requires at least the `editor` role. A key can only address tasks that `GET /api/projects/{id}/tasks` shows it — for the task, `parent_id` and `after_id`.",
        "security": [
          {
            "apiKey": [
              "tasks:update",
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskMoveBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved, or already in place",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TasksMoved"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or invalid body (`invalid_id`, `invalid_payload`), group too large (`group_too_large`), different project (`cross_project`), `schedule_membership`, `task_parent_invariant`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `schedule_requires_read_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found (`not_found`, with `task_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`after_id` no longer in the group (`sort_order_conflict`, with `task_id`), row deleted (`task_gone`), parent deleted (`parent_gone`) or concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Task deleted (`task_gone`, with `task_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}/deps": {
      "post": {
        "operationId": "createDependency",
        "tags": [
          "Dependencies"
        ],
        "summary": "Create a dependency",
        "description": "Requires at least the `editor` role. A key can only address tasks that `GET /api/projects/{id}/tasks` shows it — anything else is `404`.",
        "security": [
          {
            "apiKey": [
              "tasks:update",
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DependencyCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DependencyCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or invalid body (`invalid_id`, `invalid_payload`), invalid edge (`dep_invariant`, with `code`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `schedule_requires_read_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project or task not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Concurrent modification (`concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted (`project_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deps/{id}": {
      "delete": {
        "operationId": "deleteDependency",
        "tags": [
          "Dependencies"
        ],
        "summary": "Delete a dependency",
        "description": "Requires at least the `editor` role. The edge is deleted only if it still has the expected endpoints (otherwise `409 dep_endpoints_changed`) — after a retarget, an id can mean a different relationship. An API key MUST send both `expected_*` (`400 invalid_payload`, `code: cas_required`); the session sends both or neither.",
        "security": [
          {
            "apiKey": [
              "tasks:update",
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "The predecessor the caller saw."
            },
            "required": false,
            "name": "expected_predecessor_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "The successor the caller saw."
            },
            "required": false,
            "name": "expected_successor_id",
            "in": "query"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted",
            "headers": {
              "X-Project-Id": {
                "schema": {
                  "type": "string",
                  "description": "The project the edge belonged to (decimal number)."
                },
                "required": true
              }
            }
          },
          "400": {
            "description": "Invalid id or query (`invalid_id`, `invalid_payload`, also with `code: cas_required`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `schedule_requires_read_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Edge not found (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Endpoints changed (`dep_endpoints_changed`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted (`project_gone`) or an endpoint deleted (`dep_endpoint_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}/board": {
      "get": {
        "operationId": "getBoard",
        "tags": [
          "Board"
        ],
        "summary": "Read a project's columns and cards",
        "security": [
          {
            "apiKey": [
              "board:read"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The board",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardSnapshot"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id (`invalid_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope missing (`forbidden_api_key_scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found (`not_found`) or board disabled (`board_disabled`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted (`project_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board-items/{id}/move": {
      "post": {
        "operationId": "moveBoardItem",
        "tags": [
          "Board"
        ],
        "summary": "Move a card to a column",
        "description": "Requires at least the `editor` role. `{id}` is the card's task id. If the move touches a column in the schedule, a key also needs `tasks:update` (`403 scheduled_requires_task_scope`).",
        "security": [
          {
            "apiKey": [
              "board_items:update"
            ]
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "The card's task id."
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "task",
                "legacy"
              ],
              "description": "`task` (default): `{id}` is the task id. `legacy`: the old card id."
            },
            "required": false,
            "name": "id_space",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BoardItemMoveBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardItemMoved"
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, body or `id_space` (`invalid_id`, `invalid_payload`, `invalid_id_space`), violated rule (`board_invariant`, with `code`; `task_parent_invariant`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Role or scope missing, or read-only access (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `anchors_require_board_read`, `scheduled_requires_task_scope`, `project_read_only`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Card, column or neighbour not found (`not_found`, `column_not_found`, `neighbour_not_found`), board disabled (`board_disabled`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Stale `if_match_updated_at` or concurrent modification (`concurrent_modification`), move blocked (`detach_blocked`, `schedule_entry_blocked`, with `blockers`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project or column deleted (`project_gone`, `column_gone`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}/board-items": {
      "post": {
        "operationId": "createBoardItem",
        "tags": [
          "Board"
        ],
        "security": [
          {
            "apiKey": [
              "board_items:create"
            ]
          },
          {
            "session": []
          }
        ],
        "summary": "Create a card, optionally with dates, parent and milestone",
        "description": "Scopes: `board_items:create`; as soon as `start_date`/`end_date`/`is_milestone`/`parent_id` is present, also `tasks:create` + `tasks:read`. A key with `projects:read` and `board:read` MUST send `expect`. Not idempotent. Details: https://gantt400.com/en/developers/",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "format": "date",
              "description": "The client's calendar day (YYYY-MM-DD). Required."
            },
            "required": true,
            "name": "x-gantt400-today",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BoardItemCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardItemCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project, column or parent not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Precondition (`expect`, with `field`) or invariant violated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/projects/{id}/task-tree": {
      "post": {
        "operationId": "applyTaskTree",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "apiKey": [
              "tasks:create",
              "tasks:update",
              "tasks:read"
            ]
          },
          {
            "session": []
          }
        ],
        "summary": "Create or reconcile a subtree (idempotent via `ref`)",
        "description": "One transaction; limits 500 nodes, depth 16, 1 MiB. Scopes: `tasks:create` + `tasks:update` + `tasks:read`; if a node names `column_id`, also `board_items:create` + `board:read`. `?dry_run=1` computes everything and rolls back. Details: https://gantt400.com/en/developers/",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "dry_run",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date",
              "description": "The client's calendar day (YYYY-MM-DD) — required as soon as a NEW node without dates is sent (`400 invalid_today`)."
            },
            "required": false,
            "name": "x-gantt400-today",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskTreeBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reconciled (or computed in a dry run)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskTreeResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body; for an error in a node, with `ref`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope missing (`missing_scopes`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project or parent not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (e.g. `ref_ambiguous`, `concurrent_modification`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Project deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body larger than 1 MiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/api-keys": {
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Create an API key",
        "description": "Session only, role `admin`. The key is valid for the active team and writes with the `editor` role.",
        "security": [
          {
            "session": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`invalid_payload`) or unknown scope (`invalid_scope`, with `scope`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin, or requested with an API key (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `forbidden_api_key_cannot_manage_keys`); in the shared demo team `api_key_forbidden_in_shared_demo_tenant`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "API keys"
        ],
        "summary": "List the team's API keys",
        "description": "Session only, role `admin`. Newest first, revoked keys included.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "The keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin, or requested with an API key (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `forbidden_api_key_cannot_manage_keys`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/api-keys/{keyId}": {
      "delete": {
        "operationId": "revokeApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Revoke an API key",
        "description": "Session only, role `admin`. Takes effect immediately.",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "required": true,
            "name": "keyId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "400": {
            "description": "Invalid id (`invalid_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not an admin, or requested with an API key (`forbidden_insufficient_role`, `forbidden_api_key_scope`, `forbidden_api_key_cannot_manage_keys`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, from another team or already revoked (`not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
