{ "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": "", "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "limit", "required": false, "in": "query", "schema": { "minimum": 1, "maximum": 100, "default": 20, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } } ], "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", "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 or describe a project (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", "schema": { "$ref": "#/components/schemas/IssueArea" } }, { "name": "perColumn", "required": false, "in": "query", "schema": { "minimum": 1, "maximum": 200, "default": 50, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } } ], "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}/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, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "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/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, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "offset", "required": false, "in": "query", "schema": { "minimum": 0, "default": 0, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "status", "required": false, "in": "query", "description": "Repeat the parameter or separate with commas", "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IssueStatus" } } }, { "name": "type", "required": false, "in": "query", "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IssueType" } } }, { "name": "area", "required": false, "in": "query", "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IssueArea" } } }, { "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 BACKLOG. 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, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "offset", "required": false, "in": "query", "schema": { "minimum": 0, "default": 0, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } } ], "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, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "offset", "required": false, "in": "query", "schema": { "minimum": 0, "default": 0, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } } ], "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/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}/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, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } }, { "name": "offset", "required": false, "in": "query", "schema": { "minimum": 0, "default": 0, "allOf": [ { "$ref": "#/components/schemas/Object" } ] } } ], "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" ] } } }, "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/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 `.\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" ] }, "Object": { "type": "object", "properties": {} }, "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" ] }, "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 }, "owner": { "$ref": "#/components/schemas/UserRefDto" }, "members": { "type": "array", "items": { "$ref": "#/components/schemas/ProjectMemberDto" } }, "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", "owner", "members", "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 } }, "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 } } }, "IssueArea": { "type": "string", "enum": [ "FRONTEND", "BACKEND", "MOBILE", "QA", "DESIGN", "DEVOPS" ] }, "IssueStatus": { "type": "string", "enum": [ "BACKLOG", "TODO", "IN_PROGRESS", "IN_REVIEW", "READY_FOR_QA", "IN_QA", "QA_FAILED", "DONE", "CANCELLED" ] }, "IssueType": { "type": "string", "enum": [ "BUG", "FEATURE", "UI_CHANGE", "TASK", "QA" ] }, "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": { "allOf": [ { "$ref": "#/components/schemas/IssueType" } ] }, "area": { "type": "string", "enum": [ "FRONTEND", "BACKEND", "MOBILE", "QA", "DESIGN", "DEVOPS" ], "nullable": true }, "title": { "type": "string" }, "description": { "type": "string", "nullable": true }, "status": { "allOf": [ { "$ref": "#/components/schemas/IssueStatus" } ] }, "allowedTransitions": { "type": "array", "description": "Statuses this ticket can move to next", "items": { "$ref": "#/components/schemas/IssueStatus" } }, "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", "allowedTransitions", "priority", "reporter", "assignee", "release", "labels", "figmaUrl", "storyPoints", "dueDate", "links", "commentCount", "createdAt", "updatedAt", "version" ] }, "BoardColumnDto": { "type": "object", "properties": { "status": { "allOf": [ { "$ref": "#/components/schemas/IssueStatus" } ] }, "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": { "allOf": [ { "$ref": "#/components/schemas/IssueStatus" } ] }, "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" ] }, "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", "enum": [ "FRONTEND", "BACKEND", "MOBILE", "QA", "DESIGN", "DEVOPS" ], "nullable": true }, "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": { "allOf": [ { "$ref": "#/components/schemas/IssueType" } ] }, "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", "enum": [ "FRONTEND", "BACKEND", "MOBILE", "QA", "DESIGN", "DEVOPS" ], "nullable": true }, "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": { "allOf": [ { "$ref": "#/components/schemas/IssueType" } ] }, "title": { "type": "string", "maxLength": 255 } } }, "ChangeStatusDto": { "type": "object", "properties": { "status": { "allOf": [ { "$ref": "#/components/schemas/IssueStatus" } ] }, "reason": { "type": "string", "nullable": true, "description": "Required for QA_FAILED and CANCELLED" } }, "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" ] }, "PublishReleaseDto": { "type": "object", "properties": { "force": { "type": "object", "default": false, "description": "Publish even with unfinished tickets" } } } } } }