Files
task-stream/apps/api/openapi.json
Josep Piguillem 71dc2d6f2d fix(api): typed OpenAPI query params and not-found messages that hide private projects
- Query DTO properties with a default value (limit, offset, days, perColumn,
  assignedToMe, search, archived, requiresReason, force) were documented as
  `object` in openapi.json, which broke generated clients. They now declare
  their type explicitly.
- Reading a private project, ticket, release or its comments without access
  answered "Project <internal id> not found", confirming the project exists.
  ReadAccess now reports what the caller asked for (e2e covered).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:59:38 +02:00

6350 lines
161 KiB
JSON

{
"openapi": "3.0.0",
"paths": {
"/api/health": {
"get": {
"operationId": "HealthController_check",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"example": {
"status": "ok",
"postgres": "up",
"mongo": "up",
"redis": "up"
}
}
}
}
},
"503": {
"description": "A database is down"
}
},
"summary": "API and databases status",
"tags": [
"Health"
],
"security": []
}
},
"/api/auth/config": {
"get": {
"operationId": "AuthController_config",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AuthorizationConfigDto"
}
}
}
}
},
"summary": "How to sign in with Pireon (null when not configured)",
"tags": [
"Auth"
],
"security": []
}
},
"/api/auth/token": {
"post": {
"description": "For browsers (Pireon has no CORS) and Swagger UI: adds the client secret server-side. Apps with their own Pireon OAuth client can get tokens from Pireon directly; any valid Pireon access token is accepted by this API.",
"operationId": "AuthController_token",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssueTokenDto"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/IssueTokenDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenResponseDto"
}
}
}
},
"401": {
"description": "Pireon rejected the grant"
}
},
"summary": "Exchange a grant for Pireon tokens (proxy of Pireon /oauth/token)",
"tags": [
"Auth"
],
"security": []
}
},
"/api/users/me": {
"get": {
"operationId": "UsersController_me",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MeDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "The user behind the access token",
"tags": [
"Users"
]
}
},
"/api/users": {
"get": {
"description": "Needs the `users:read` scope in the access token.",
"operationId": "UsersController_search",
"parameters": [
{
"name": "search",
"required": false,
"in": "query",
"description": "Name or email",
"schema": {
"default": "",
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 100,
"default": 20,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/UserDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Search Pireon users (to assign tickets or add members)",
"tags": [
"Users"
]
}
},
"/api/users/{id}": {
"get": {
"operationId": "UsersController_findOne",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "A Pireon user",
"tags": [
"Users"
]
}
},
"/api/webhooks/pireon": {
"post": {
"description": "`user.deactivated` closes the user's sessions; `token.revoked` forgets the token. Disabled unless PIREON_WEBHOOK_SECRET is set.",
"operationId": "PireonWebhookController_receive",
"parameters": [
{
"name": "x-webhook-signature",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "X-Webhook-Signature",
"in": "header",
"description": "HMAC-SHA256 of the raw body with PIREON_WEBHOOK_SECRET (hex)",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"204": {
"description": "Processed"
},
"401": {
"description": "Missing or invalid signature"
}
},
"summary": "Pireon events (called by Pireon, not by apps)",
"tags": [
"Webhooks"
],
"security": []
}
},
"/api/projects": {
"get": {
"operationId": "ProjectsController_mine",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ProjectDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Projects where I am a member",
"tags": [
"Projects"
]
},
"post": {
"operationId": "ProjectsController_create",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateProjectDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Create a project (you become its owner)",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}": {
"get": {
"operationId": "ProjectsController_findOne",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProjectDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "A project with its members and settings",
"tags": [
"Projects"
]
},
"patch": {
"operationId": "ProjectsController_update",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateProjectDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Rename, describe or make public/private a project (owner/admin)",
"tags": [
"Projects"
]
},
"delete": {
"description": "Deletes the project and, right after (asynchronously, through a saga), its tickets, comments and releases; links from tickets of other projects are removed and the key becomes free. Cannot be undone from the API. `confirm` must be the project key.",
"operationId": "ProjectsController_remove",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "confirm",
"required": true,
"in": "query",
"description": "The project key, to confirm the deletion",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Delete a project (owner only)",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/settings": {
"put": {
"description": "Replaces the whole configuration (send what GET /projects/{project} returns, changed). Values can be added, renamed, reordered or archived, never removed: tickets and statistics keep referring to them.",
"operationId": "ProjectsController_updateSettings",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProjectSettingsBodyDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Statuses, transitions, ticket types and areas (owner/admin)",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/board": {
"get": {
"operationId": "ProjectsController_board",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "releaseId",
"required": false,
"in": "query",
"description": "Only this release",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "assigneeId",
"required": false,
"in": "query",
"description": "Only this person (Pireon user id)",
"schema": {
"type": "string"
}
},
{
"name": "area",
"required": false,
"in": "query",
"description": "An area id",
"schema": {
"example": "FRONTEND",
"type": "string"
}
},
{
"name": "perColumn",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BoardColumnDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Kanban board: one column per status",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/stats": {
"get": {
"operationId": "ProjectsController_stats",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProjectStatsDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Tracking dashboard: counts by status, type, person…",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/analytics": {
"get": {
"description": "Created vs resolved per week, cumulative flow per day, cycle and lead time, throughput per person and QA pass/fail.",
"operationId": "ProjectsController_analytics",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "days",
"required": false,
"in": "query",
"schema": {
"minimum": 7,
"maximum": 365,
"default": 90,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProjectAnalyticsDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Statistics of the last `days` days",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/activity": {
"get": {
"operationId": "ProjectsController_activity",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "issueId",
"required": false,
"in": "query",
"description": "Only this ticket",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "before",
"required": false,
"in": "query",
"description": "Cursor: entries older than this date",
"schema": {
"format": "date-time",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ActivityDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Activity feed (newest first)",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/members": {
"post": {
"description": "The user must exist and be active in Pireon.",
"operationId": "ProjectsController_addMember",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddMemberDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Add a Pireon user to the project (owner/admin)",
"tags": [
"Projects"
]
}
},
"/api/projects/{project}/members/{userId}": {
"patch": {
"operationId": "ProjectsController_changeMemberRole",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "userId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeMemberRoleDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Change a member's role (owner/admin)",
"tags": [
"Projects"
]
},
"delete": {
"operationId": "ProjectsController_removeMember",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "userId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Remove a member (owner/admin, or yourself to leave)",
"tags": [
"Projects"
]
}
},
"/api/me/issues": {
"get": {
"description": "Assigned to me by default; with assignedToMe=false, any ticket of my projects (e.g. waiting for QA).",
"operationId": "IssuesController_mine",
"parameters": [
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"schema": {
"minimum": 0,
"default": 0,
"type": "number"
}
},
{
"name": "assignedToMe",
"required": false,
"in": "query",
"description": "true: tickets assigned to me. false: any ticket of my projects (e.g. ?assignedToMe=false&category=QA)",
"schema": {
"default": true,
"type": "boolean"
}
},
{
"name": "status",
"required": false,
"in": "query",
"description": "Status ids. Repeat the parameter or separate with commas",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "category",
"required": false,
"in": "query",
"description": "Whatever each project calls its statuses",
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/StatusCategory"
}
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssuePageDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "My work: tickets across all my projects",
"tags": [
"Issues"
]
}
},
"/api/projects/{project}/issues": {
"get": {
"operationId": "IssuesController_search",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"schema": {
"minimum": 0,
"default": 0,
"type": "number"
}
},
{
"name": "status",
"required": false,
"in": "query",
"description": "Status ids. Repeat the parameter or separate with commas",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "category",
"required": false,
"in": "query",
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/StatusCategory"
}
}
},
{
"name": "type",
"required": false,
"in": "query",
"description": "Ticket type ids",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "area",
"required": false,
"in": "query",
"description": "Area ids",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "priority",
"required": false,
"in": "query",
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssuePriority"
}
}
},
{
"name": "assigneeId",
"required": false,
"in": "query",
"description": "Pireon user id",
"schema": {
"type": "string"
}
},
{
"name": "unassigned",
"required": false,
"in": "query",
"description": "Only tickets without assignee",
"schema": {
"type": "boolean"
}
},
{
"name": "releaseId",
"required": false,
"in": "query",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "label",
"required": false,
"in": "query",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "search",
"required": false,
"in": "query",
"description": "Matches key, title or description",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssuePageDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Search the tickets of a project",
"tags": [
"Issues"
]
},
"post": {
"description": "Starts in the project's first active TODO-category status (BACKLOG by default). The response carries its key (e.g. WEB-13).",
"operationId": "IssuesController_create",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateIssueDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Create a ticket",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}": {
"get": {
"operationId": "IssuesController_findOne",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssueDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "A ticket",
"tags": [
"Issues"
]
},
"patch": {
"operationId": "IssuesController_update",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateIssueDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Edit title, description, type, priority, area, labels…",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/status": {
"put": {
"description": "Only the transitions in `allowedTransitions` are accepted (422 otherwise). QA_FAILED and CANCELLED need a reason.",
"operationId": "IssuesController_changeStatus",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Move the ticket through the workflow (incl. QA)",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/assignee": {
"put": {
"operationId": "IssuesController_assign",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AssignIssueDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Assign to a project member (null to unassign)",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/release": {
"put": {
"operationId": "IssuesController_setRelease",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SetIssueReleaseDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Put the ticket in a planned release (null takes it out)",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/links": {
"post": {
"description": "e.g. WEB-3 DEPENDS_ON API-7, WEB-9 TESTS WEB-3. May cross projects.",
"operationId": "IssuesController_link",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LinkIssueDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Relate tickets (FE ↔ BE ↔ QA)",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/links/{type}/{target}": {
"delete": {
"operationId": "IssuesController_unlink",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
},
{
"name": "type",
"required": true,
"in": "path",
"schema": {
"$ref": "#/components/schemas/IssueLinkType"
}
},
{
"name": "target",
"required": true,
"in": "path",
"description": "Other ticket: id or key",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Remove a relation",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/comments": {
"get": {
"operationId": "IssuesController_listComments",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"schema": {
"minimum": 0,
"default": 0,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommentPageDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Comments of the ticket (oldest first)",
"tags": [
"Issues"
]
},
"post": {
"operationId": "IssuesController_addComment",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommentBodyDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Comment on the ticket",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/history": {
"get": {
"operationId": "IssuesController_history",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"schema": {
"minimum": 0,
"default": 0,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ActivityDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "History of the ticket (newest first)",
"tags": [
"Issues"
]
}
},
"/api/comments/{commentId}": {
"patch": {
"operationId": "CommentsController_edit",
"parameters": [
{
"name": "commentId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommentBodyDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Edit a comment (author only)",
"tags": [
"Comments"
]
},
"delete": {
"operationId": "CommentsController_delete",
"parameters": [
{
"name": "commentId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Delete a comment (author or project admin)",
"tags": [
"Comments"
]
}
},
"/api/me/releases": {
"get": {
"operationId": "ReleasesController_upcoming",
"parameters": [
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 50,
"default": 10,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ReleaseDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Planned releases of all my projects, soonest target date first",
"tags": [
"Releases"
]
}
},
"/api/projects/{project}/releases": {
"get": {
"operationId": "ReleasesController_list",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ReleaseDto"
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Releases of a project",
"tags": [
"Releases"
]
},
"post": {
"operationId": "ReleasesController_create",
"parameters": [
{
"name": "project",
"required": true,
"in": "path",
"description": "Project id or key",
"schema": {
"example": "WEB",
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateReleaseDto"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Plan a release (owner/admin)",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}": {
"get": {
"operationId": "ReleasesController_findOne",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReleaseDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "A release",
"tags": [
"Releases"
]
},
"patch": {
"operationId": "ReleasesController_update",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateReleaseDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Rename, describe or move the target date",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}/progress": {
"get": {
"operationId": "ReleasesController_progress",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReleaseProgressDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Finished tickets out of the total",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}/burnup": {
"get": {
"operationId": "ReleasesController_burnup",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReleaseBurnupDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Burnup: tickets in the release and finished ones, day by day",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}/issues": {
"get": {
"operationId": "ReleasesController_issuesOf",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "limit",
"required": false,
"in": "query",
"schema": {
"minimum": 1,
"maximum": 200,
"default": 50,
"type": "number"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"schema": {
"minimum": 0,
"default": 0,
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssuePageDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
},
{}
],
"summary": "Tickets included in the release",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}/publish": {
"post": {
"description": "Fails (422) while it has unfinished tickets unless `force` is true.",
"operationId": "ReleasesController_publish",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublishReleaseDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Ship the release",
"tags": [
"Releases"
]
}
},
"/api/releases/{releaseId}/archive": {
"post": {
"operationId": "ReleasesController_archive",
"parameters": [
{
"name": "releaseId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Archive a planned release (its tickets leave it)",
"tags": [
"Releases"
]
}
},
"/api/explore/projects": {
"get": {
"description": "Their board, tickets, comments, releases, activity and statistics can be read without a token too.",
"operationId": "ExploreController_publicProjects",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ProjectDto"
}
}
}
}
}
},
"summary": "Public projects (no token needed)",
"tags": [
"Explore"
],
"security": []
}
}
},
"info": {
"title": "Pireon TaskStream API",
"description": "REST API of **Pireon TaskStream** for other apps: projects, tickets (bugs, features,\nUI changes, tasks and QA), workflow, comments, relations and releases.\n\n### Authentication\nEvery endpoint (except `/auth`, `/health` and the webhook) needs a **Pireon access\ntoken**, so any app or user that consumes it goes through Pireon's OAuth:\n\n- **Authorization Code + PKCE** against `oauth.pireon.dev/oauth/authorize`\n (the \"Authorize\" button here uses it; register `<api>/api/docs/oauth2-redirect.html`\n as a redirect URI of the client).\n- An app with its own Pireon OAuth client can get tokens from Pireon directly and\n send them as `Authorization: Bearer <token>`.\n\nWhat you can see and change depends on your role in each project.\n\n### Writes are eventually consistent\nCommands answer `{ id, key?, version }` as soon as the event is stored. Reads come from\na read model that catches up in milliseconds: a `GET` right after a write may still\nshow the previous version (or 404 right after a create).\n\n### Real time\nFor live updates use the GraphQL subscriptions at `/graphql` (graphql-ws).",
"version": "1.0.0",
"contact": {}
},
"tags": [
{
"name": "Projects",
"description": "Projects, members, board, stats and activity"
},
{
"name": "Issues",
"description": "Tickets: search, create, workflow, relations, comments"
},
{
"name": "Comments",
"description": "Edit and delete comments"
},
{
"name": "Releases",
"description": "Versions that group tickets"
},
{
"name": "Users",
"description": "The signed-in user and the Pireon directory"
},
{
"name": "Auth",
"description": "Pireon OAuth token proxy"
},
{
"name": "Health",
"description": "Liveness of the API and its databases"
},
{
"name": "Webhooks",
"description": "Called by Pireon"
}
],
"servers": [
{
"url": "/",
"description": "This server (the paths already include /api)"
}
],
"components": {
"securitySchemes": {
"pireon-oauth": {
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://oauth.pireon.dev/oauth/authorize",
"tokenUrl": "/api/auth/token",
"refreshUrl": "/api/auth/token",
"scopes": {
"users:read": "Search Pireon users"
}
},
"password": {
"tokenUrl": "/api/auth/token",
"refreshUrl": "/api/auth/token",
"scopes": {
"users:read": "Search Pireon users"
}
}
},
"description": "Pireon OAuth2 (Laravel Passport)"
},
"pireon": {
"scheme": "bearer",
"bearerFormat": "JWT",
"type": "http",
"description": "A Pireon access token obtained elsewhere"
}
},
"schemas": {
"AuthorizationConfigDto": {
"type": "object",
"properties": {
"authorizeUrl": {
"type": "string",
"example": "https://oauth.pireon.dev/oauth/authorize"
},
"clientId": {
"type": "string",
"nullable": true,
"description": "Public id of the authorization_code client (\"Sign in with Pireon\"); null when only email + password is configured"
},
"scopes": {
"example": [
"users:read"
],
"type": "array",
"items": {
"type": "string"
}
},
"passwordGrant": {
"type": "boolean",
"description": "Whether email + password sign-in is allowed"
}
},
"required": [
"authorizeUrl",
"clientId",
"scopes",
"passwordGrant"
]
},
"IssueTokenDto": {
"type": "object",
"properties": {
"grant_type": {
"type": "string",
"enum": [
"authorization_code",
"password",
"refresh_token"
]
},
"code": {
"type": "string",
"description": "authorization_code: code returned by Pireon"
},
"code_verifier": {
"type": "string",
"description": "authorization_code: PKCE verifier"
},
"redirect_uri": {
"type": "string",
"description": "authorization_code: redirect URI used to authorize"
},
"username": {
"type": "string",
"description": "password: Pireon email"
},
"password": {
"type": "string",
"description": "password: Pireon password"
},
"refresh_token": {
"type": "string",
"description": "refresh_token"
}
},
"required": [
"grant_type"
]
},
"TokenResponseDto": {
"type": "object",
"properties": {
"access_token": {
"type": "string"
},
"refresh_token": {
"type": "string",
"nullable": true
},
"expires_in": {
"type": "number",
"description": "Seconds"
},
"token_type": {
"type": "string",
"example": "Bearer"
}
},
"required": [
"access_token",
"refresh_token",
"expires_in",
"token_type"
]
},
"MeDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Pireon user id"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"isActive": {
"type": "boolean"
},
"roles": {
"description": "Global roles in Pireon",
"type": "array",
"items": {
"type": "string"
}
},
"appRoles": {
"description": "Roles inside the task-stream OAuth client",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"name",
"email",
"isActive",
"roles",
"appRoles"
]
},
"ApiErrorDto": {
"type": "object",
"properties": {
"statusCode": {
"type": "number",
"example": 404
},
"code": {
"type": "string",
"description": "Domain error code",
"enum": [
"NOT_FOUND",
"FORBIDDEN",
"VALIDATION",
"CONFLICT",
"BUSINESS_RULE"
]
},
"message": {
"description": "Human message (a list of messages for invalid input)",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"example": "Issue not found"
}
},
"required": [
"statusCode",
"message"
]
},
"UserDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Pireon user id"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"isActive": {
"type": "boolean"
}
},
"required": [
"id",
"name",
"email",
"isActive"
]
},
"UserRefDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Pireon user id"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
]
},
"ProjectRole": {
"type": "string",
"enum": [
"OWNER",
"ADMIN",
"MEMBER",
"VIEWER"
]
},
"ProjectMemberDto": {
"type": "object",
"properties": {
"user": {
"$ref": "#/components/schemas/UserRefDto"
},
"role": {
"allOf": [
{
"$ref": "#/components/schemas/ProjectRole"
}
]
}
},
"required": [
"user",
"role"
]
},
"StatusCategory": {
"type": "string",
"enum": [
"TODO",
"IN_PROGRESS",
"QA",
"DONE",
"CANCELLED"
]
},
"SettingColor": {
"type": "string",
"enum": [
"slate",
"stone",
"violet",
"blue",
"sky",
"teal",
"green",
"amber",
"orange",
"red",
"pink"
]
},
"WorkflowStatusDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "IN_REVIEW",
"description": "Stable id on tickets"
},
"name": {
"type": "string",
"example": "En revisión"
},
"category": {
"allOf": [
{
"$ref": "#/components/schemas/StatusCategory"
}
]
},
"color": {
"allOf": [
{
"$ref": "#/components/schemas/SettingColor"
}
]
},
"requiresReason": {
"type": "boolean",
"description": "Entering it needs a reason"
},
"archived": {
"type": "boolean",
"description": "No new tickets or transitions into it"
}
},
"required": [
"id",
"name",
"category",
"color",
"requiresReason",
"archived"
]
},
"StatusTransitionsDto": {
"type": "object",
"properties": {
"from": {
"type": "string",
"example": "IN_QA"
},
"to": {
"example": [
"DONE",
"QA_FAILED"
],
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"from",
"to"
]
},
"TypeIcon": {
"type": "string",
"enum": [
"bug",
"feature",
"palette",
"task",
"qa",
"flag",
"star-fill",
"rocket",
"document",
"light-bulb",
"key",
"support"
]
},
"IssueTypeOptionDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "BUG"
},
"name": {
"type": "string",
"example": "Bug"
},
"icon": {
"allOf": [
{
"$ref": "#/components/schemas/TypeIcon"
}
]
},
"color": {
"allOf": [
{
"$ref": "#/components/schemas/SettingColor"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"id",
"name",
"icon",
"color",
"archived"
]
},
"AreaOptionDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "FRONTEND"
},
"name": {
"type": "string",
"example": "Frontend"
},
"archived": {
"type": "boolean"
}
},
"required": [
"id",
"name",
"archived"
]
},
"ProjectSettingsDto": {
"type": "object",
"properties": {
"statuses": {
"description": "In board order",
"type": "array",
"items": {
"$ref": "#/components/schemas/WorkflowStatusDto"
}
},
"transitions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/StatusTransitionsDto"
}
},
"issueTypes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssueTypeOptionDto"
}
},
"areas": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AreaOptionDto"
}
}
},
"required": [
"statuses",
"transitions",
"issueTypes",
"areas"
]
},
"ProjectDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"key": {
"type": "string",
"example": "WEB",
"description": "Prefix of the ticket keys"
},
"name": {
"type": "string",
"example": "Checkout revamp"
},
"description": {
"type": "string",
"nullable": true
},
"isPublic": {
"type": "boolean",
"description": "Readable by anyone, also without a token"
},
"owner": {
"$ref": "#/components/schemas/UserRefDto"
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ProjectMemberDto"
}
},
"settings": {
"$ref": "#/components/schemas/ProjectSettingsDto"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"version": {
"type": "number",
"description": "Version of the aggregate (event count)"
}
},
"required": [
"id",
"key",
"name",
"description",
"isPublic",
"owner",
"members",
"settings",
"createdAt",
"updatedAt",
"version"
]
},
"CreateProjectDto": {
"type": "object",
"properties": {
"key": {
"type": "string",
"example": "WEB",
"description": "2-10 letters/digits starting with a letter → tickets WEB-1, WEB-2…"
},
"name": {
"type": "string",
"example": "Checkout revamp",
"maxLength": 120
},
"description": {
"type": "string",
"nullable": true,
"maxLength": 5000
},
"isPublic": {
"type": "boolean",
"default": false,
"description": "Anyone can read it without signing in (board, tickets, comments, releases, activity, statistics)"
}
},
"required": [
"key",
"name"
]
},
"CommandResultDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Id of the aggregate written"
},
"key": {
"type": "string",
"description": "Human key when one was assigned (project or issue)",
"example": "WEB-13"
},
"version": {
"type": "number",
"description": "Version after the write. Writes are acknowledged at once and the read model catches up asynchronously: a GET may briefly return the previous version (or 404 right after a create)."
}
},
"required": [
"id",
"version"
]
},
"UpdateProjectDto": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "Checkout revamp",
"maxLength": 120
},
"description": {
"type": "string",
"nullable": true,
"maxLength": 5000
},
"isPublic": {
"type": "boolean",
"default": false,
"description": "Anyone can read it without signing in (board, tickets, comments, releases, activity, statistics)"
}
}
},
"WorkflowStatusBodyDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "IN_REVIEW",
"description": "Never change an existing id"
},
"name": {
"type": "string",
"example": "En revisión",
"maxLength": 40
},
"category": {
"allOf": [
{
"$ref": "#/components/schemas/StatusCategory"
}
]
},
"color": {
"allOf": [
{
"$ref": "#/components/schemas/SettingColor"
}
]
},
"requiresReason": {
"type": "boolean",
"default": false
},
"archived": {
"type": "boolean",
"default": false
}
},
"required": [
"id",
"name",
"category",
"color"
]
},
"StatusTransitionsBodyDto": {
"type": "object",
"properties": {
"from": {
"type": "string",
"example": "IN_QA"
},
"to": {
"example": [
"DONE",
"QA_FAILED"
],
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"from",
"to"
]
},
"IssueTypeOptionBodyDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "SPIKE"
},
"name": {
"type": "string",
"example": "Spike",
"maxLength": 40
},
"icon": {
"allOf": [
{
"$ref": "#/components/schemas/TypeIcon"
}
]
},
"color": {
"allOf": [
{
"$ref": "#/components/schemas/SettingColor"
}
]
},
"archived": {
"type": "boolean",
"default": false
}
},
"required": [
"id",
"name",
"icon",
"color"
]
},
"AreaOptionBodyDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "DATA"
},
"name": {
"type": "string",
"example": "Datos",
"maxLength": 40
},
"archived": {
"type": "boolean",
"default": false
}
},
"required": [
"id",
"name"
]
},
"ProjectSettingsBodyDto": {
"type": "object",
"properties": {
"statuses": {
"description": "In board order. Values cannot be removed: archive them instead.",
"type": "array",
"items": {
"$ref": "#/components/schemas/WorkflowStatusBodyDto"
}
},
"transitions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/StatusTransitionsBodyDto"
}
},
"issueTypes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssueTypeOptionBodyDto"
}
},
"areas": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AreaOptionBodyDto"
}
}
},
"required": [
"statuses",
"transitions",
"issueTypes",
"areas"
]
},
"IssuePriority": {
"type": "string",
"enum": [
"LOWEST",
"LOW",
"MEDIUM",
"HIGH",
"CRITICAL"
]
},
"ReleaseRefDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string",
"example": "v1.4.0"
}
},
"required": [
"id",
"name"
]
},
"IssueLinkType": {
"type": "string",
"enum": [
"BLOCKS",
"DEPENDS_ON",
"TESTS",
"DUPLICATES",
"RELATES_TO"
]
},
"LinkDirection": {
"type": "string",
"enum": [
"OUTWARD",
"INWARD"
],
"description": "OUTWARD: this issue → other. INWARD: other → this issue."
},
"IssueLinkDto": {
"type": "object",
"properties": {
"type": {
"allOf": [
{
"$ref": "#/components/schemas/IssueLinkType"
}
]
},
"direction": {
"description": "OUTWARD: this issue → other. INWARD: other → this issue.",
"allOf": [
{
"$ref": "#/components/schemas/LinkDirection"
}
]
},
"issueId": {
"type": "string",
"format": "uuid"
},
"issueKey": {
"type": "string",
"example": "WEB-3"
}
},
"required": [
"type",
"direction",
"issueId",
"issueKey"
]
},
"IssueDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"projectId": {
"type": "string",
"format": "uuid"
},
"projectKey": {
"type": "string",
"example": "WEB"
},
"number": {
"type": "number",
"example": 12
},
"key": {
"type": "string",
"example": "WEB-12",
"description": "Human key"
},
"type": {
"type": "string",
"example": "BUG",
"description": "A ticket type id"
},
"area": {
"type": "string",
"nullable": true,
"example": "FRONTEND"
},
"title": {
"type": "string"
},
"description": {
"type": "string",
"nullable": true
},
"status": {
"type": "string",
"example": "IN_PROGRESS",
"description": "A status id"
},
"statusCategory": {
"allOf": [
{
"$ref": "#/components/schemas/StatusCategory"
}
]
},
"allowedTransitions": {
"description": "Statuses this ticket can move to next (project's workflow)",
"type": "array",
"items": {
"type": "string"
}
},
"priority": {
"allOf": [
{
"$ref": "#/components/schemas/IssuePriority"
}
]
},
"reporter": {
"$ref": "#/components/schemas/UserRefDto"
},
"assignee": {
"nullable": true,
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/UserRefDto"
}
]
},
"release": {
"nullable": true,
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/ReleaseRefDto"
}
]
},
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
"figmaUrl": {
"type": "string",
"nullable": true,
"description": "Figma frame"
},
"storyPoints": {
"type": "number",
"nullable": true
},
"dueDate": {
"type": "string",
"nullable": true,
"format": "date"
},
"links": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssueLinkDto"
}
},
"commentCount": {
"type": "number"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"version": {
"type": "number"
}
},
"required": [
"id",
"projectId",
"projectKey",
"number",
"key",
"type",
"area",
"title",
"description",
"status",
"statusCategory",
"allowedTransitions",
"priority",
"reporter",
"assignee",
"release",
"labels",
"figmaUrl",
"storyPoints",
"dueDate",
"links",
"commentCount",
"createdAt",
"updatedAt",
"version"
]
},
"BoardColumnDto": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "IN_PROGRESS"
},
"issues": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssueDto"
}
},
"total": {
"type": "number",
"description": "Tickets in the column (may exceed `issues`)"
}
},
"required": [
"status",
"issues",
"total"
]
},
"CountByStatusDto": {
"type": "object",
"properties": {
"key": {
"type": "string",
"example": "IN_PROGRESS",
"description": "Status id"
},
"count": {
"type": "number"
}
},
"required": [
"key",
"count"
]
},
"CountByKeyDto": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Type, priority or area (\"NONE\" = unset)"
},
"count": {
"type": "number"
}
},
"required": [
"key",
"count"
]
},
"CountByAssigneeDto": {
"type": "object",
"properties": {
"user": {
"nullable": true,
"description": "Null = unassigned",
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/UserRefDto"
}
]
},
"count": {
"type": "number"
}
},
"required": [
"user",
"count"
]
},
"ProjectStatsDto": {
"type": "object",
"properties": {
"total": {
"type": "number"
},
"open": {
"type": "number",
"description": "Not done nor cancelled"
},
"byStatus": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByStatusDto"
}
},
"byType": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByKeyDto"
}
},
"byPriority": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByKeyDto"
}
},
"byArea": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByKeyDto"
}
},
"byAssignee": {
"description": "Open tickets per person",
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByAssigneeDto"
}
}
},
"required": [
"total",
"open",
"byStatus",
"byType",
"byPriority",
"byArea",
"byAssignee"
]
},
"CreatedResolvedDto": {
"type": "object",
"properties": {
"week": {
"type": "string",
"format": "date",
"description": "Monday of the week"
},
"created": {
"type": "number"
},
"resolved": {
"type": "number"
}
},
"required": [
"week",
"created",
"resolved"
]
},
"FlowSeriesDto": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "IN_PROGRESS"
},
"counts": {
"description": "One value per date",
"type": "array",
"items": {
"type": "number"
}
}
},
"required": [
"status",
"counts"
]
},
"CumulativeFlowDto": {
"type": "object",
"properties": {
"dates": {
"type": "array",
"items": {
"type": "string",
"format": "date"
}
},
"series": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FlowSeriesDto"
}
}
},
"required": [
"dates",
"series"
]
},
"DurationStatsDto": {
"type": "object",
"properties": {
"count": {
"type": "number"
},
"averageDays": {
"type": "number",
"nullable": true
},
"p85Days": {
"type": "number",
"nullable": true,
"description": "85% of tickets take at most this long"
}
},
"required": [
"count",
"averageDays",
"p85Days"
]
},
"ThroughputDto": {
"type": "object",
"properties": {
"week": {
"type": "string",
"format": "date"
},
"user": {
"nullable": true,
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/UserRefDto"
}
]
},
"count": {
"type": "number"
}
},
"required": [
"week",
"user",
"count"
]
},
"QaStatsDto": {
"type": "object",
"properties": {
"passed": {
"type": "number",
"description": "Left QA to a done status"
},
"failed": {
"type": "number",
"description": "Left QA back to work"
},
"failureRate": {
"type": "number",
"nullable": true,
"minimum": 0,
"maximum": 1
}
},
"required": [
"passed",
"failed",
"failureRate"
]
},
"ProjectAnalyticsDto": {
"type": "object",
"properties": {
"from": {
"type": "string",
"format": "date-time"
},
"to": {
"type": "string",
"format": "date-time"
},
"createdVsResolved": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CreatedResolvedDto"
}
},
"cumulativeFlow": {
"description": "Tickets in each status at the end of every day",
"allOf": [
{
"$ref": "#/components/schemas/CumulativeFlowDto"
}
]
},
"cycleTime": {
"description": "Start of work → done",
"allOf": [
{
"$ref": "#/components/schemas/DurationStatsDto"
}
]
},
"leadTime": {
"description": "Creation → done",
"allOf": [
{
"$ref": "#/components/schemas/DurationStatsDto"
}
]
},
"throughput": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ThroughputDto"
}
},
"qa": {
"$ref": "#/components/schemas/QaStatsDto"
}
},
"required": [
"from",
"to",
"createdVsResolved",
"cumulativeFlow",
"cycleTime",
"leadTime",
"throughput",
"qa"
]
},
"ActivityDto": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"example": "IssueStatusChanged"
},
"projectId": {
"type": "string",
"format": "uuid"
},
"issueId": {
"type": "string",
"nullable": true
},
"issueKey": {
"type": "string",
"nullable": true,
"example": "WEB-12"
},
"actor": {
"$ref": "#/components/schemas/UserRefDto"
},
"summary": {
"type": "string",
"example": "Ana movió WEB-12 de En curso a En revisión"
},
"occurredAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"id",
"type",
"projectId",
"issueId",
"issueKey",
"actor",
"summary",
"occurredAt"
]
},
"AddMemberDto": {
"type": "object",
"properties": {
"userId": {
"type": "string",
"description": "Pireon user id (see GET /users)"
},
"role": {
"default": "MEMBER",
"allOf": [
{
"$ref": "#/components/schemas/ProjectRole"
}
]
}
},
"required": [
"userId"
]
},
"ChangeMemberRoleDto": {
"type": "object",
"properties": {
"role": {
"allOf": [
{
"$ref": "#/components/schemas/ProjectRole"
}
]
}
},
"required": [
"role"
]
},
"IssuePageDto": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IssueDto"
}
},
"total": {
"type": "number",
"description": "Total matches (for pagination)"
}
},
"required": [
"items",
"total"
]
},
"CreateIssueDto": {
"type": "object",
"properties": {
"description": {
"type": "string",
"nullable": true,
"maxLength": 50000
},
"area": {
"type": "string",
"nullable": true,
"example": "FRONTEND",
"description": "An area id of the project's settings"
},
"priority": {
"allOf": [
{
"$ref": "#/components/schemas/IssuePriority"
}
]
},
"labels": {
"maxItems": 20,
"type": "array",
"items": {
"type": "string"
}
},
"figmaUrl": {
"type": "string",
"nullable": true,
"description": "Figma frame (https) for UI changes"
},
"storyPoints": {
"type": "number",
"nullable": true,
"minimum": 0,
"maximum": 100
},
"dueDate": {
"type": "string",
"nullable": true,
"format": "date"
},
"type": {
"type": "string",
"example": "BUG",
"description": "A ticket type id of the project's settings"
},
"title": {
"type": "string",
"example": "El botón de pagar no responde en Safari",
"maxLength": 255
},
"assigneeId": {
"type": "string",
"nullable": true,
"description": "Pireon user id"
},
"releaseId": {
"type": "string",
"nullable": true,
"format": "uuid"
}
},
"required": [
"type",
"title"
]
},
"UpdateIssueDto": {
"type": "object",
"properties": {
"description": {
"type": "string",
"nullable": true,
"maxLength": 50000
},
"area": {
"type": "string",
"nullable": true,
"example": "FRONTEND",
"description": "An area id of the project's settings"
},
"priority": {
"allOf": [
{
"$ref": "#/components/schemas/IssuePriority"
}
]
},
"labels": {
"maxItems": 20,
"type": "array",
"items": {
"type": "string"
}
},
"figmaUrl": {
"type": "string",
"nullable": true,
"description": "Figma frame (https) for UI changes"
},
"storyPoints": {
"type": "number",
"nullable": true,
"minimum": 0,
"maximum": 100
},
"dueDate": {
"type": "string",
"nullable": true,
"format": "date"
},
"type": {
"type": "string",
"example": "FEATURE",
"description": "A ticket type id"
},
"title": {
"type": "string",
"maxLength": 255
}
}
},
"ChangeStatusDto": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "IN_REVIEW",
"description": "A status id of the project's settings, among the ticket's allowedTransitions"
},
"reason": {
"type": "string",
"nullable": true,
"description": "Required by statuses with requiresReason (e.g. QA_FAILED)"
}
},
"required": [
"status"
]
},
"AssignIssueDto": {
"type": "object",
"properties": {
"assigneeId": {
"type": "string",
"nullable": true,
"description": "Pireon user id of a project member, or null to unassign"
}
},
"required": [
"assigneeId"
]
},
"SetIssueReleaseDto": {
"type": "object",
"properties": {
"releaseId": {
"type": "string",
"nullable": true,
"format": "uuid",
"description": "Planned release, or null to take the ticket out"
}
},
"required": [
"releaseId"
]
},
"LinkIssueDto": {
"type": "object",
"properties": {
"type": {
"allOf": [
{
"$ref": "#/components/schemas/IssueLinkType"
}
]
},
"targetIssue": {
"type": "string",
"example": "API-3",
"description": "Other ticket: id or key"
}
},
"required": [
"type",
"targetIssue"
]
},
"CommentDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"issueId": {
"type": "string",
"format": "uuid"
},
"author": {
"$ref": "#/components/schemas/UserRefDto"
},
"body": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"editedAt": {
"type": "string",
"nullable": true,
"format": "date-time"
},
"version": {
"type": "number"
}
},
"required": [
"id",
"issueId",
"author",
"body",
"createdAt",
"editedAt",
"version"
]
},
"CommentPageDto": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CommentDto"
}
},
"total": {
"type": "number"
}
},
"required": [
"items",
"total"
]
},
"CommentBodyDto": {
"type": "object",
"properties": {
"body": {
"type": "string",
"example": "Reproducido en Safari 17.",
"maxLength": 20000
}
},
"required": [
"body"
]
},
"ReleaseStatus": {
"type": "string",
"enum": [
"PLANNED",
"RELEASED",
"ARCHIVED"
]
},
"ReleaseDto": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"projectId": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string",
"example": "v1.4.0"
},
"description": {
"type": "string",
"nullable": true
},
"targetDate": {
"type": "string",
"nullable": true,
"format": "date"
},
"status": {
"allOf": [
{
"$ref": "#/components/schemas/ReleaseStatus"
}
]
},
"releasedAt": {
"type": "string",
"nullable": true,
"format": "date-time"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"version": {
"type": "number"
}
},
"required": [
"id",
"projectId",
"name",
"description",
"targetDate",
"status",
"releasedAt",
"createdAt",
"version"
]
},
"CreateReleaseDto": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "v1.4.0",
"maxLength": 60
},
"description": {
"type": "string",
"nullable": true,
"maxLength": 5000
},
"targetDate": {
"type": "string",
"nullable": true,
"format": "date",
"example": "2026-11-14"
}
},
"required": [
"name"
]
},
"UpdateReleaseDto": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "v1.4.0",
"maxLength": 60
},
"description": {
"type": "string",
"nullable": true,
"maxLength": 5000
},
"targetDate": {
"type": "string",
"nullable": true,
"format": "date",
"example": "2026-11-14"
}
}
},
"ReleaseProgressDto": {
"type": "object",
"properties": {
"total": {
"type": "number"
},
"finished": {
"type": "number"
},
"percent": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"byStatus": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CountByStatusDto"
}
}
},
"required": [
"total",
"finished",
"percent",
"byStatus"
]
},
"ReleaseBurnupDto": {
"type": "object",
"properties": {
"releaseId": {
"type": "string",
"format": "uuid"
},
"targetDate": {
"type": "string",
"nullable": true,
"format": "date"
},
"dates": {
"type": "array",
"items": {
"type": "string",
"format": "date"
}
},
"scope": {
"description": "Tickets in the release (not cancelled)",
"type": "array",
"items": {
"type": "number"
}
},
"done": {
"type": "array",
"items": {
"type": "number"
}
}
},
"required": [
"releaseId",
"targetDate",
"dates",
"scope",
"done"
]
},
"PublishReleaseDto": {
"type": "object",
"properties": {
"force": {
"type": "boolean",
"default": false,
"description": "Publish even with unfinished tickets"
}
}
}
}
}
}