openapi: 3.0.0
info:
  title: Weblate
  version: latest
paths:
  /api/v2/ai-translator:
    post:
      tags:
        - AiTranslator
      summary: 'Crate ai translator'
      description: 'crate ai translator'
      operationId: createAiTranslator
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
                - token
                - apiUrl
                - promptTokenRate
                - completionTokenRate
                - languages
              properties:
                type:
                  type: string
                  example: ChatGPT
                name:
                  type: string
                  example: ChatGPT_Default
                token:
                  type: string
                  example: 'api token'
                apiUrl:
                  type: string
                  example: 'https://example.com'
                promptTokenRate:
                  type: number
                  format: float
                  example: 0.0005
                completionTokenRate:
                  type: number
                  format: float
                  example: 0.0005
                languages:
                  type: array
                  items: { type: integer, example: 1 }
              type: object
      responses:
        '201':
          description: 'Ai translator created successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { name: { type: array, items: { type: string, example: 'Ai translator with the same name already exists' } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-translators/{id}':
    get:
      tags:
        - AiTranslator
      summary: 'Get ai translator by id'
      description: 'Fetches one ai translator'
      operationId: getOneAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }
                  type: { type: string, example: ChatGPT }
                  name: { type: string, example: ChatGPT }
                  apiUrl: { type: string, example: 'https://test.com' }
                  promptTokenRate: { type: number, format: float, example: 0.0005 }
                  completionTokenRate: { type: number, format: float, example: 0.0005 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  context: { type: string, example: 'Default prompt context', nullable: true }
                  createdAt: { type: string, example: '2025-12-12 00:00:00' }
                  updatedAt: { type: string, example: '2025-12-12 00:00:00' }
                  deletedAt: { type: string, example: '2025-12-12 00:00:00' }
                  actions: { type: array, items: { type: string, enum: [AiTranslatorUpdate, AiTranslatorDelete] }, example: [AiTranslatorUpdate, AiTranslatorDelete] }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - AiTranslator
      summary: 'Update ai translator'
      description: 'update ai translator'
      operationId: updateAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
                - token
                - apiUrl
                - promptTokenRate
                - completionTokenRate
                - languages
              properties:
                type:
                  type: string
                  example: ChatGPT
                name:
                  type: string
                  example: ChatGPTDefault
                token:
                  type: string
                  example: 'api token'
                apiUrl:
                  type: string
                  example: 'https://example.com'
                promptTokenRate:
                  type: number
                  format: float
                  example: 0.0005
                completionTokenRate:
                  type: number
                  format: float
                  example: 0.0005
                languages:
                  type: array
                  items: { type: integer, example: 1 }
              type: object
      responses:
        '200':
          description: 'Successful response with an updated ai translator'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }
                  type: { type: string, example: ChatGPT }
                  name: { type: string, example: ChatGPT }
                  apiUrl: { type: string, example: 'https://test.com' }
                  promptTokenRate: { type: number, format: float, example: 0.0005 }
                  completionTokenRate: { type: number, format: float, example: 0.0005 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  context: { type: string, example: 'Default prompt context', nullable: true }
                  createdAt: { type: string, example: '2025-12-12 00:00:00' }
                  updatedAt: { type: string, example: '2025-12-12 00:00:00' }
                  deletedAt: { type: string, example: '2025-12-12 00:00:00' }
                  actions: { type: array, items: { type: string, enum: [AiTranslatorUpdate, AiTranslatorDelete] }, example: [AiTranslatorUpdate, AiTranslatorDelete] }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { name: { type: array, items: { type: string, example: 'Ai translator with the same name already exists' } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - AiTranslator
      summary: 'Soft delete ai translator'
      description: 'soft delete ai translator'
      operationId: deleteAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      responses:
        '200':
          description: 'Ai translator soft deleted successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/ai-translators:
    get:
      tags:
        - AiTranslator
      summary: 'Get ai translators'
      description: 'Fetches all ai translators without pagination'
      operationId: getAllAiTranslator
      responses:
        '200':
          description: 'Successful response with a list of ai translators'
          content:
            application/json:
              schema:
                properties:
                  aiTranslators: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, type: { type: string, example: ChatGPT }, name: { type: string, example: ChatGPT }, apiUrl: { type: string, example: 'https://test.com' }, promptTokenRate: { type: number, format: float, example: 0.0005 }, completionTokenRate: { type: number, format: float, example: 0.0005 }, languages: { type: array, items: { type: integer, example: 1 } }, createdAt: { type: string, example: '2025-12-12 00:00:00' }, updatedAt: { type: string, example: '2025-12-12 00:00:00' }, deletedAt: { type: string, example: '2025-12-12 00:00:00' }, actions: { type: array, items: { type: string, enum: [AiTranslatorUpdate, AiTranslatorDelete] }, example: [AiTranslatorUpdate, AiTranslatorDelete] } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/ai-types:
    get:
      tags:
        - AiTranslator
      summary: 'Get ai types'
      description: 'Fetches all ai types'
      operationId: getAllAiTypes
      responses:
        '200':
          description: 'Successful response with a list of ai types'
          content:
            application/json:
              schema:
                properties:
                  aiTypes: { type: array, items: { type: string, enum: [ChatGPT] } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/ai-usage:
    get:
      tags:
        - AiTranslator
      summary: 'Get ai usage'
      description: 'Fetches ai usage by type(project or project type), item id, ?year and ?month'
      operationId: getAiUsage
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of project types to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: type
          in: query
          description: 'Project(1) or project type(2)'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: aiTranslatorId
          in: query
          description: 'Id of ai translator'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: itemId
          in: query
          description: 'Id of project or project type'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: languageId
          in: query
          description: 'Language id'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: year
          in: query
          description: Year
          required: false
          schema:
            type: integer
          example: 2025
        -
          name: month
          in: query
          description: Month
          required: false
          schema:
            type: integer
          example: 5
        -
          name: sort
          in: query
          description: "Filter projects by sort(\n                -promptTokenRate,promptTokenRate,\n                -completionTokenRate,completionTokenRate,\n                -price,price,\n                -year,year\n                )"
          required: false
          schema:
            type: string
          example: '-price'
      responses:
        '200':
          description: 'Successful response with a list of ai usage by type'
          content:
            application/json:
              schema:
                properties:
                  aiUsage: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, type: { type: integer, example: 1 }, year: { type: integer, example: 2025 }, month: { type: integer, example: 4 }, price: { type: number, format: float, example: 17 }, completionTokenCount: { type: integer, example: 10 }, promptTokenCount: { type: integer, example: 30 }, pricePrompt: { type: number, format: float, example: 8.5 }, priceCompletion: { type: number, format: float, example: 8.5 }, language: { type: string, example: Russian } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { type: { type: array, items: { type: string, example: 'Type is required' } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-products/{id}/ai-tenants':
    get:
      tags:
        - AiTranslator
      summary: 'List product tenants with AI status + usage'
      description: "Lists the product's tenants with AI on/off status and monthly usage (tokens + cost). Report drill-down under a product."
      operationId: getProductAiTenants
      parameters:
        -
          name: id
          in: path
          description: 'Product UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: date
          in: query
          description: 'Month to report (YYYY-MM-DD)'
          required: true
          schema:
            type: string
          example: '2026-07-01'
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 30
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: name
          in: query
          description: 'Filter by tenant name (ILIKE)'
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sort: name,-name,price,-price'
          required: false
          schema:
            type: string
          example: '-price'
        -
          name: aiTranslatorId
          in: query
          description: 'Scope usage (tokens/cost) to this AI translator UUID'
          required: false
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'List of tenants with AI status + usage'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, name: { type: string, example: DEMO }, aiTranslatorId: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112, nullable: true }, status: { properties: { id: { type: integer, example: 0 }, name: { type: string, example: 'Off' } }, type: object }, promptTokenCount: { type: integer, example: 12000 }, completionTokenCount: { type: integer, example: 8000 }, tokenCount: { type: integer, example: 20000 }, price: { type: number, format: float, example: 5.11 }, lastUpdate: { type: string, example: '2026-07-14 14:27:25', nullable: true } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 4 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Product not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/ai-products':
    get:
      tags:
        - AiTranslator
      summary: 'List workspace products with AI status + usage'
      description: "Lists the workspace's products with AI on/off status and monthly usage (tokens + cost). Doubles as the per-workspace usage report top level."
      operationId: getWorkspaceAiProducts
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: date
          in: query
          description: 'Month to report (YYYY-MM-DD)'
          required: true
          schema:
            type: string
          example: '2026-07-01'
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 30
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: name
          in: query
          description: 'Filter by product name (ILIKE)'
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sort: name,-name,price,-price'
          required: false
          schema:
            type: string
          example: '-price'
        -
          name: aiTranslatorId
          in: query
          description: 'Scope usage (tokens/cost) to this AI translator UUID'
          required: false
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'List of products with AI status + usage'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, name: { type: string, example: b2core }, aiTranslatorId: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112, nullable: true }, status: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: 'On' } }, type: object }, promptTokenCount: { type: integer, example: 12000 }, completionTokenCount: { type: integer, example: 8000 }, tokenCount: { type: integer, example: 20000 }, price: { type: number, format: float, example: 5.11 }, lastUpdate: { type: string, example: '2026-07-14 14:27:25', nullable: true } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 12 } }, type: object }
                  workspaceAiTranslatorId: { description: 'The workspace default AI translator (copied into new items at creation; NOT a runtime fallback).', type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Workspace not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/ai-translators':
    get:
      tags:
        - AiTranslator
      summary: 'List AI translators for a workspace admin'
      description: 'Lists the AI translator catalog available to a workspace admin (for the AI report filter and the default-translator modal). Same data as GET /api/v2/ai-translators, gated to super-admin OR the workspace admin.'
      operationId: getWorkspaceAiTranslators
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 200
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: 'List of AI translators'
          content:
            application/json:
              schema:
                properties:
                  aiTranslators: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, name: { type: string, example: ChatGPT } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 2 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-translators/{id}/assignment-remove':
    post:
      tags:
        - AiTranslator
      summary: 'Delete ai translator assignment'
      description: 'delete ai translator assignment'
      operationId: deleteAiTranslatorAssignment
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - type
                - itemId
              properties:
                type:
                  description: '1(tenant) or 2(product)'
                  type: integer
                  example: 1
                itemId:
                  type: string
                  example: d3cb3f65-5b8b-4620-8480-5165621e1112
              type: object
      responses:
        '200':
          description: 'Ai translator assignment removed successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { itemId: { type: string, example: 'Invalid item' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/ai-translator':
    put:
      tags:
        - AiTranslator
      summary: 'Set workspace default AI translator'
      description: "Sets the workspace's single default AI translator. This value is copied into new tenants/products at creation time; it is NOT a runtime fallback. Pass aiTranslatorId=null to clear the default."
      operationId: setWorkspaceAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - aiTranslatorId
              properties:
                aiTranslatorId:
                  description: 'AI translator UUID, or null to clear the workspace default.'
                  type: string
                  format: uuid
                  example: d3cb3f65-5b8b-4620-8480-5165621e1112
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Workspace default AI translator updated'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Invalid ai translator'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Workspace not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-products/{id}/ai-status':
    patch:
      tags:
        - AiTranslator
      summary: 'Toggle product AI on/off'
      description: 'Turns AI on or off for a product. ON restores or creates an ai_translator_assignment pointing at the workspace default; OFF soft-deletes it. Product- and tenant-level toggles are independent.'
      operationId: toggleProductAi
      parameters:
        -
          name: id
          in: path
          description: 'Product UUID'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - enabled
              properties:
                enabled:
                  description: 'true = AI on, false = AI off'
                  type: boolean
                  example: true
              type: object
      responses:
        '200':
          description: 'AI status updated'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Validation error (e.g. workspace has no default AI translator)'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Product not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-tenants/{id}/ai-status':
    patch:
      tags:
        - AiTranslator
      summary: 'Toggle tenant AI on/off'
      description: 'Turns AI on or off for a tenant. ON restores or creates an ai_translator_assignment pointing at the workspace default; OFF soft-deletes it. Tenant- and product-level toggles are independent.'
      operationId: toggleTenantAi
      parameters:
        -
          name: id
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - enabled
              properties:
                enabled:
                  description: 'true = AI on, false = AI off'
                  type: boolean
                  example: true
              type: object
      responses:
        '200':
          description: 'AI status updated'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Validation error (e.g. workspace has no default AI translator)'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Tenant not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-translators/{id}/assignment':
    post:
      tags:
        - AiTranslator
      summary: 'Update ai translator assignment'
      description: 'update ai translator assignment'
      operationId: updateAiTranslatorAssignment
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - type
                - itemId
              properties:
                type:
                  description: '1(tenant) or 2(product)'
                  type: integer
                  example: 1
                itemId:
                  type: string
                  example: d3cb3f65-5b8b-4620-8480-5165621e1112
              type: object
      responses:
        '200':
          description: 'Ai translator assignment updated successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { itemId: { type: string, example: 'Invalid item' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/ai-translators/{id}/context':
    patch:
      tags:
        - AiTranslator
      summary: 'Update AI translator context'
      description: 'Updates AI translator context'
      operationId: updateAiTranslatorContext
      parameters:
        -
          name: id
          in: path
          description: 'The ai translator id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - context
              properties:
                context:
                  type: string
                  example: 'Default prompt context'
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Successful response with an updated ai translator'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }
                  type: { type: string, example: ChatGPT }
                  name: { type: string, example: ChatGPT }
                  apiUrl: { type: string, example: 'https://test.com' }
                  promptTokenRate: { type: number, format: float, example: 0.0005 }
                  completionTokenRate: { type: number, format: float, example: 0.0005 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  context: { type: string, example: 'Default prompt context', nullable: true }
                  createdAt: { type: string, example: '2025-12-12 00:00:00' }
                  updatedAt: { type: string, example: '2025-12-12 00:00:00' }
                  deletedAt: { type: string, example: '2025-12-12 00:00:00' }
                  actions: { type: array, items: { type: string, enum: [AiTranslatorUpdate, AiTranslatorDelete] }, example: [AiTranslatorUpdate, AiTranslatorDelete] }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { context: { type: array, items: { type: string, example: 'Invalid context value' } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/auth/sso:
    post:
      tags:
        - Auth
      summary: 'Exchange Keycloak code for B2Translate tokens'
      description: 'Exchanges a Keycloak authorization code for B2Translate access/refresh tokens. Implements the BFF half of the OpenID Connect Authorization Code Flow.'
      operationId: loginWithSso
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - code
                - redirect_uri
              properties:
                code:
                  type: string
                  example: eyJhbGciOi...
                redirect_uri:
                  type: string
                  example: 'http://localhost:4200/auth/sso/callback'
              type: object
      responses:
        '200':
          description: Authenticated
          content:
            application/json:
              schema:
                properties:
                  access_token: { type: string }
                  refresh_token: { type: string }
                  expires_in: { description: 'Access token lifetime in seconds', type: integer }
                  refresh_expires_in: { description: 'Refresh token lifetime in seconds', type: integer }
                type: object
        '400':
          description: 'Invalid authorization code'
        '401':
          description: 'Invalid id_token'
        '403':
          description: 'User is not registered in B2Translate'
  /api/v2/orchestrator/events:
    post:
      tags:
        - B2Store
      summary: 'Accept a B2STORE orchestrator event'
      description: 'Accepts any B2STORE orchestrator event, validates the envelope and queues it for asynchronous processing. Access is restricted by an exact-IP allowlist (B2STORE_ALLOWED_IPS) and, when configured, a bearer token (B2STORE_WEBHOOK_SECRET). Answering 202 means the event is durably queued, not that provisioning finished. Re-delivering the same event_id is safe and also answers 202.'
      operationId: acceptOrchestratorEvent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - event_id
                - event_type
              properties:
                schema_version:
                  type: integer
                  example: 1
                event_id:
                  type: string
                  format: uuid
                  example: 550e8400-e29b-41d4-a716-446655440000
                event_type:
                  type: string
                  example: product.purchased
                timestamp:
                  type: string
                  format: date-time
                  example: '2026-04-01T10:15:00Z'
                source:
                  type: string
                  example: b2store
                product_slug:
                  type: string
                  example: b2core
                subscription_id:
                  type: string
                  format: uuid
                  example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                customer:
                  properties: { user_id: { type: string, format: uuid }, email: { type: string, example: jane@example.com }, name: { type: string, example: 'Jane Smith' }, organization_id: { type: string, format: uuid } }
                  type: object
                payload:
                  description: 'Event-type specific body. Validated by the consumer, not here.'
                  type: object
              type: object
      responses:
        '202':
          description: 'Event queued (or already known — see `duplicate`)'
          content:
            application/json:
              schema:
                properties:
                  accepted: { type: boolean, example: true }
                  eventId: { type: string, format: uuid }
                  duplicate: { type: boolean, example: false }
                type: object
        '400':
          description: 'Malformed envelope'
        '403':
          description: 'Caller not allowed'
        '500':
          description: 'Server Error'
  /api/v2/backoffice/tenants:
    get:
      tags:
        - 'Backoffice Tenant'
      summary: 'Get all tenants grouped by product'
      operationId: c6cc9ecb2715116782cb54e6a2098f50
      parameters:
        -
          name: productId
          in: query
          schema:
            type: string
        -
          name: productName
          in: query
          schema:
            type: string
        -
          name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
        -
          name: offset
          in: query
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        '200':
          description: 'List of tenants with available and existing platforms'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string }, name: { type: string }, platforms: { type: array, items: { properties: { id: { type: integer, nullable: true }, name: { type: string }, platform: { type: string }, projectTypeId: { type: integer, nullable: true }, uuid: { type: string, nullable: true }, changerId: { type: integer, nullable: true }, createdAt: { type: string, format: date-time, nullable: true }, deletedAt: { type: string, format: date-time, nullable: true } }, type: object } }, availablePlatforms: { type: array, items: { type: string } }, productPlatforms: { type: array, items: { type: string } }, productId: { type: string }, productName: { type: string }, changerId: { type: integer, nullable: true }, createdAt: { type: string, format: date-time, nullable: true }, updatedAt: { type: string, format: date-time, nullable: true }, deletedAt: { type: string, format: date-time, nullable: true } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer }, limit: { type: integer }, offset: { type: integer } }, type: object }
                type: object
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - 'Backoffice Tenant'
      summary: 'Create tenant via robot token'
      operationId: 300a21e6b699459b87b3374c976a67ba
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - tenantId
                - productId
                - name
                - platforms
              properties:
                id:
                  type: string
                  format: uuid
                productId:
                  type: string
                  format: uuid
                name:
                  type: string
                platforms:
                  type: array
                  items: { type: string }
                languages:
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '200':
          description: 'Tenant created'
          content:
            application/json:
              schema:
                properties:
                  tenantId: { type: string, format: uuid }
                type: object
        '409':
          description: 'Tenant with provided ID already exists'
      security:
        -
          BearerAuthObject: []
  '/api/v2/backoffice/tenants/{id}':
    get:
      tags:
        - 'Backoffice Tenant'
      summary: 'Get tenant details'
      operationId: c9042de4901e7c80a143fa0c1ba2749d
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'Tenant detail'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string }
                  name: { type: string }
                  platforms: { type: array, items: { properties: { id: { type: integer, nullable: true }, name: { type: string }, platform: { type: string }, projectTypeId: { type: integer, nullable: true }, uuid: { type: string, nullable: true }, changerId: { type: integer, nullable: true }, createdAt: { type: string, format: date-time, nullable: true }, deletedAt: { type: string, format: date-time, nullable: true } }, type: object } }
                  languages: { type: array, items: { properties: { id: { type: integer }, name: { type: string, nullable: true }, lang: { type: string }, langBcp: { type: string, example: en-US, nullable: true }, dir: { type: string, example: ltr, nullable: true } }, type: object } }
                  hasCustomLanguageOrder: { type: boolean }
                  availablePlatforms: { type: array, items: { type: string } }
                  productPlatforms: { type: array, items: { type: string } }
                  productId: { type: string }
                  productName: { type: string }
                  changerId: { type: integer, nullable: true }
                  createdAt: { type: string, format: date-time, nullable: true }
                  updatedAt: { type: string, format: date-time, nullable: true }
                  deletedAt: { type: string, format: date-time, nullable: true }
                type: object
        '404':
          description: 'Tenant not found'
    put:
      tags:
        - 'Backoffice Tenant'
      summary: 'Update tenant via robot token'
      operationId: 56fa630ec39e9edb091b15f12622e676
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - productId
                - name
                - platforms
              properties:
                productId:
                  type: string
                  format: uuid
                name:
                  type: string
                platforms:
                  type: array
                  items: { type: string }
                languages:
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '200':
          description: 'Tenant updated'
          content:
            application/json:
              schema:
                properties:
                  tenantId: { type: string, format: uuid }
                type: object
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - 'Backoffice Tenant'
      summary: 'Delete tenant'
      operationId: 133e918a05e168954704152a6c28f561
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'Tenant deleted'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string }
                type: object
      security:
        -
          BearerAuthObject: []
  /api/v2/keys-version:
    get:
      tags:
        - Backoffice
      summary: 'Get keys version list'
      description: 'Returns a paginated list of keys_version records. Accessible to SuperAdmin and above.'
      operationId: getKeysVersion
      parameters:
        -
          name: tenantId
          in: query
          description: 'Filter by tenant UUID'
          required: false
          schema:
            type: string
            format: uuid
        -
          name: platform
          in: query
          description: 'Filter by platform (case-insensitive)'
          required: false
          schema:
            type: string
            example: web
        -
          name: langBcp
          in: query
          description: 'Filter by BCP-47 language code (case-insensitive)'
          required: false
          schema:
            type: string
            example: en
        -
          name: storyName
          in: query
          description: 'Filter by story name (case-insensitive)'
          required: false
          schema:
            type: string
            example: production
        -
          name: includeDeleted
          in: query
          description: 'Include soft-deleted records (default: false)'
          required: false
          schema:
            type: boolean
            default: false
        -
          name: limit
          in: query
          description: 'Number of records per page'
          required: false
          schema:
            type: integer
            default: 50
            minimum: 1
        -
          name: offset
          in: query
          description: 'Number of records to skip'
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        '200':
          description: 'Paginated list of keys_version records'
          content:
            application/json:
              schema:
                properties:
                  items: { type: array, items: { properties: { tenantId: { type: string, format: uuid }, platform: { type: string, example: web }, langBcp: { type: string, example: en }, storyName: { type: string, example: production }, projectTypeId: { type: integer }, version: { type: integer, example: 42 }, updatedAt: { type: string, format: date-time }, deletedAt: { type: string, format: date-time, nullable: true }, operationId: { type: string }, changerId: { type: integer } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer }, limit: { type: integer }, offset: { type: integer } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/backoffice/languages:
    get:
      tags:
        - 'Backoffice Tenant'
      summary: 'Get all languages for backoffice'
      operationId: bace159ba0f751c545194d540619cbe3
      responses:
        '200':
          description: 'List of languages'
          content:
            application/json:
              schema:
                properties:
                  languages: { type: array, items: { properties: { id: { type: integer }, name: { type: string, nullable: true }, lang: { type: string }, langBcp: { type: string, nullable: true }, dir: { type: string, example: ltr, nullable: true }, pluralForms: { type: array, items: { type: string }, nullable: true } }, type: object } }
                type: object
  /api/v2/cache/invalidation-history:
    get:
      tags:
        - Cache
      summary: 'Get invalidation history'
      description: 'Get paginated cache invalidation history'
      operationId: getCacheInvalidationHistory
      parameters:
        -
          name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
            maximum: 1500
            minimum: 1
        -
          name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        '200':
          description: 'Paginated invalidation history'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: string, format: uuid }, userId: { type: integer }, userEmail: { type: string }, invalidatedAt: { type: string, format: date-time }, type: { type: string, enum: [by_tenant, by_product] }, tenantId: { type: string, format: uuid, nullable: true }, tenantName: { type: string, nullable: true }, productId: { type: string, format: uuid, nullable: true }, productName: { type: string, nullable: true }, platform: { type: string }, storyName: { type: string }, keysDeleted: { type: integer }, metadata: { type: object, nullable: true } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/cache/invalidate/by-product:
    post:
      tags:
        - Cache
      summary: 'Invalidate cache by product'
      description: 'Invalidate Redis cache for /api/v3/keys by product and platform'
      operationId: invalidateCacheByProduct
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - productId
                - platform
                - storyName
              properties:
                productId:
                  type: string
                  format: uuid
                  example: 01936f1a-5b5a-7000-8000-000000000001
                platform:
                  description: 'Bucket (platform) name. Free string, scoped per product.'
                  type: string
                  example: web
                storyName:
                  type: string
                  example: production
              type: object
      responses:
        '200':
          description: 'Cache invalidated successfully'
          content:
            application/json:
              schema:
                properties:
                  keysDeleted: { type: integer, example: 24 }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { field: { type: string, example: 'Error message' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/cache/invalidate/by-tenant:
    post:
      tags:
        - Cache
      summary: 'Invalidate cache by tenant'
      description: 'Invalidate Redis cache for /api/v3/keys by tenant and platform'
      operationId: invalidateCacheByTenant
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - tenantId
                - platform
                - storyName
              properties:
                tenantId:
                  type: string
                  format: uuid
                  example: 01936f1a-5b5a-7000-8000-000000000001
                platform:
                  description: 'Bucket (platform) name. Free string, scoped per product.'
                  type: string
                  example: web
                storyName:
                  type: string
                  example: production
              type: object
      responses:
        '200':
          description: 'Cache invalidated successfully'
          content:
            application/json:
              schema:
                properties:
                  keysDeleted: { type: integer, example: 12 }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { field: { type: string, example: 'Error message' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/categories/project-type/{projectTypeId}':
    get:
      tags:
        - Categories
      summary: 'Get categories by project type'
      description: 'Retrieves all categories by project type.'
      operationId: getCategoriesByProjectTypeAndStory
      parameters:
        -
          name: projectTypeId
          in: path
          description: 'Project type identifier'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                properties:
                  categories: { type: array, items: { properties: { id: { type: integer, example: 101 }, name: { type: string, example: 'Category.php Name' }, mask: { type: string, example: mask-example }, all_sources_count: { type: integer, example: 5 }, actions: { type: array, items: { type: string, example: CATEGORY_UPDATE } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: 'Access denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/categories/merge-preview:
    get:
      tags:
        - Categories
      summary: 'Get merge preview categories'
      description: 'Returns categories from source story project type with count of keys that exist in source but not in target story for given language.'
      operationId: getMergePreviewCategories
      parameters:
        -
          name: sourceStoryId
          in: query
          description: 'Source story ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: targetStoryId
          in: query
          description: 'Target story ID'
          required: true
          schema:
            type: integer
          example: 2
        -
          name: languageId
          in: query
          description: 'Language ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Search by category name (case-insensitive)'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Merge preview categories list'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: default }, newKeysCount: { type: integer, example: 5 } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 12 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/categories/by-platform/{platformId}':
    get:
      tags:
        - Categories
      summary: 'Get categories and platform categories by platformId'
      description: 'Returns categories and platform categories by platform ID.'
      operationId: getPlatformCategories
      parameters:
        -
          name: platformId
          in: path
          required: true
          schema:
            type: integer
            example: 76
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                properties:
                  platformCategories: { type: array, items: { type: string }, example: [default] }
                  allCategories: { type: array, items: { type: string }, example: ['default, auth'] }
                type: object
        '400':
          description: 'Bad Request'
        '401':
          description: Unauthorized
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/glossary/by-project-type/{id}':
    get:
      tags:
        - Glossary
      summary: 'Get glossary by project type'
      description: 'Returns all glossary terms with translations for a given project type.'
      operationId: getGlossary
      parameters:
        -
          name: id
          in: path
          description: 'Project type identifier'
          required: true
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Glossary terms with translations'
          content:
            application/json:
              schema:
                properties:
                  items: { type: array, items: { properties: { id: { type: string, format: uuid }, sourceTerm: { type: string, example: Account }, translations: { type: array, items: { properties: { languageId: { type: integer, example: 1 }, lang: { type: string, example: en }, translatedTerm: { type: string, example: Account } }, type: object } } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - Glossary
      summary: 'Replace glossary'
      description: 'Full replace of glossary terms for a given project type. Deletes all existing terms and inserts new ones.'
      operationId: updateGlossary
      parameters:
        -
          name: id
          in: path
          description: 'Project type identifier'
          required: true
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                terms:
                  type: array
                  items: { properties: { sourceTerm: { type: string, example: Account }, translations: { type: array, items: { properties: { languageId: { type: integer, example: 1 }, translatedTerm: { type: string, example: Cuenta } }, type: object } } }, type: object }
              type: object
      responses:
        '200':
          description: 'Glossary replaced successfully'
          content:
            application/json:
              schema:
                properties:
                  projectTypeId: { type: integer, example: 1 }
                  count: { type: integer, example: 5 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/intercom/token:
    get:
      tags:
        - Intercom
      summary: 'Get an Intercom identity token for the current user'
      description: 'Mints a signed identity-verification token for booting the Intercom Messenger widget with the current user. Returns {"enabled": false} on any environment with no Intercom secret configured — this is the normal shape everywhere except where DevOps has wired one, and the frontend must simply skip loading the widget in that case rather than treat it as an error.'
      operationId: getIntercomToken
      responses:
        '200':
          description: 'Token minted, or Intercom disabled on this environment'
          content:
            application/json:
              schema:
                properties:
                  enabled: { type: boolean }
                  appId: { type: string, nullable: true }
                  token: { type: string, nullable: true }
                  userId: { type: string, example: b2translate-42, nullable: true }
                  email: { type: string, nullable: true }
                  name: { type: string, nullable: true }
                  createdAt: { description: 'Unix seconds', type: integer, nullable: true }
                type: object
        '401':
          description: 'Not authenticated'
  /api/v2/languages:
    get:
      tags:
        - Language
      summary: 'Get all languages'
      description: 'Fetches all languages with optional parameters for pagination, sorting, and filtering by name or language code.'
      operationId: getAllLanguages
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of languages to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: sort
          in: query
          description: 'Sort by field. Available options: "name", "-name", "lang", "-lang"'
          required: false
          schema:
            type: string
          example: '-name'
        -
          name: search
          in: query
          description: 'Filter languages by name or language code'
          required: false
          schema:
            type: string
          example: ar
      responses:
        '200':
          description: 'Successful response with a list of languages'
          content:
            application/json:
              schema:
                properties:
                  languages: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: Arabic }, lang: { type: string, example: ar }, langBcp: { type: string, example: ar-AE, nullable: true }, dir: { type: string, example: ltr }, projectsCount: { type: integer, example: 5 }, actions: { type: array, items: { type: string, enum: [LanguageCreate, LanguageUpdate, LanguageDelete], example: LanguageCreate } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Language
      summary: 'Create language'
      description: 'Create a new language'
      operationId: createLanguage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                name:
                  type: string
                  example: German
                lang:
                  type: string
                  example: de
                langBcp:
                  type: string
                  example: de-DE
                dir:
                  type: string
                  example: ltr
                context:
                  type: string
                  example: null
                  nullable: true
                pluralForms:
                  type: string
                  example: null
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Successful response with created language'
          content:
            application/json:
              schema:
                properties:
                  id: { type: integer, example: 10 }
                  name: { type: string, example: German }
                  lang: { type: string, example: de }
                  langBcp: { type: string, example: de-DE }
                  dir: { type: string, example: ltr }
                  context: { type: string, example: null, nullable: true }
                  pluralForms: { type: string, example: null, nullable: true }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/languages/{id}':
    delete:
      tags:
        - Language
      summary: 'Delete language'
      description: 'Delete a language'
      operationId: deleteLanguage
      parameters:
        -
          name: id
          in: path
          description: 'The language Id'
          required: true
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Language deleted successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Conflict — language is used by a tenant or product'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    patch:
      tags:
        - Language
      summary: 'Update language'
      description: 'Update an existing language'
      operationId: updateLanguage
      parameters:
        -
          name: id
          in: path
          description: 'The language Id'
          required: true
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                name:
                  type: string
                  example: German
                lang:
                  type: string
                  example: de
                langBcp:
                  type: string
                  example: de-DE
                dir:
                  type: string
                  example: ltr
                context:
                  type: string
                  example: null
                  nullable: true
                pluralForms:
                  type: string
                  example: null
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Successful response with updated language'
          content:
            application/json:
              schema:
                properties:
                  id: { type: integer, example: 10 }
                  name: { type: string, example: German }
                  lang: { type: string, example: de }
                  langBcp: { type: string, example: de-DE }
                  dir: { type: string, example: ltr }
                  context: { type: string, example: null, nullable: true }
                  pluralForms: { type: string, example: null, nullable: true }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/languages/by-project-type/{id}':
    get:
      tags:
        - Language
      summary: 'Get languages by project type'
      description: 'Returns languages list by project type id (based on product.languages).'
      operationId: getLanguagesByProjectType
      parameters:
        -
          name: id
          in: path
          description: 'Project type id'
          required: true
          schema:
            type: integer
            example: 1
      responses:
        '200':
          description: 'List of languages'
          content:
            application/json:
              schema:
                properties:
                  languages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English }, lang: { type: string, example: en }, langBcp: { type: string, example: en-US, nullable: true }, dir: { type: string, example: ltr, nullable: true }, pluralForms: { properties: { cardinal: { type: array, items: { type: string }, nullable: true }, ordinal: { type: array, items: { type: string }, nullable: true } }, type: object, nullable: true } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/languages/by-story/{fromStoryId}':
    get:
      tags:
        - Language
      summary: 'Get languages by story pair'
      description: 'Returns languages list with merge counts between two stories.'
      operationId: getLanguagesByStory
      parameters:
        -
          name: fromStoryId
          in: path
          description: 'Source story id'
          required: true
          schema:
            type: integer
            example: 1
        -
          name: toStoryId
          in: query
          description: 'Target story id'
          required: true
          schema:
            type: integer
            example: 2
      responses:
        '200':
          description: 'List of languages with merge counts'
          content:
            application/json:
              schema:
                properties:
                  languages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English }, lang: { type: string, example: en }, langBcp: { type: string, example: en-US }, dir: { type: string, example: ltr }, newSourceCount: { type: integer, example: 12 }, updatedSourceCount: { type: integer, example: 5 }, sourceCount: { type: integer, example: 17 } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/languages/simple:
    get:
      tags:
        - Language
      summary: 'Get all languages (simple list)'
      description: 'Returns a paginated list of all languages with id, name, lang and langBcp fields. Default limit is 100.'
      operationId: getSimpleLanguages
      parameters:
        -
          name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 100
            maximum: 1500
            minimum: 10
        -
          name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        '200':
          description: 'Successful response with a simple paginated list of languages'
          content:
            application/json:
              schema:
                properties:
                  languages: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: Arabic }, lang: { type: string, example: ar }, langBcp: { type: string, example: ar } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 36 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/languages/by-tenant/{tenantId}':
    get:
      tags:
        - Language
      summary: 'Get tenant and all languages'
      description: 'Returns both tenant-assigned languages and all available languages in the system.'
      operationId: getTenantLanguagesAndAll
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant ID (string)'
          required: true
          schema:
            type: string
            example: 44749aa9-f33d-4ea0-accb-7a3bde3c8c3e
      responses:
        '200':
          description: 'Tenant and all languages'
          content:
            application/json:
              schema:
                properties:
                  tenantLanguages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object } }
                  allLanguages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/notification-recipients':
    get:
      tags:
        - Notification
      summary: 'List notification recipients'
      description: 'Lists explicit notification recipients for the workspace. Accessible by workspace-admin and super roles.'
      operationId: listNotificationRecipients
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      responses:
        '200':
          description: 'Recipient list'
          content:
            application/json:
              schema:
                type: array
                items:
                  properties: { id: { type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f }, userId: { type: integer, example: 42 }, userEmail: { type: string, example: user@example.com, nullable: true }, userName: { type: string, example: 'Jane Doe', nullable: true }, productId: { description: 'null = workspace-wide scope', type: string, example: null, nullable: true }, productName: { type: string, example: null, nullable: true }, projectTypeId: { description: 'platform id; null = whole product / workspace-wide', type: integer, example: null, nullable: true }, projectTypeName: { type: string, example: null, nullable: true } }
                  type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Notification
      summary: 'Add notification recipient (batch)'
      description: 'Batch-assigns a user to one or more notification scopes (one row per scope). Each scope: {productId:null, projectTypeId:null} = workspace-wide; {productId, projectTypeId:null} = whole product; {productId, projectTypeId} = specific platform. Existing/duplicate scopes are skipped (idempotent). Accessible by workspace-admin and super roles.'
      operationId: addNotificationRecipient
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - userId
                - scopes
              properties:
                userId:
                  type: integer
                  example: 42
                scopes:
                  description: 'Workspace-wide = a single scope {productId:null, projectTypeId:null}.'
                  type: array
                  items: { required: [productId, projectTypeId], properties: { productId: { description: 'null = workspace-wide scope', type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f, nullable: true }, projectTypeId: { description: 'platform id; null = whole product / workspace-wide', type: integer, example: null, nullable: true } }, type: object }
              type: object
      responses:
        '201':
          description: 'Created — ids of the rows created by this batch (skipped scopes are omitted)'
          content:
            application/json:
              schema:
                properties:
                  ids: { type: array, items: { type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f } }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/notification-recipients/{recipientId}':
    delete:
      tags:
        - Notification
      summary: 'Delete notification recipient'
      description: 'Removes a notification recipient. Accessible by workspace-admin and super roles.'
      operationId: deleteNotificationRecipient
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
        -
          name: recipientId
          in: path
          description: 'Recipient UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e60
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/notification-routing':
    get:
      tags:
        - Notification
      summary: 'Get notification event routing'
      description: 'Returns the event -> channel[] routing map plus the available event catalog and channels. Accessible by workspace-admin and super roles.'
      operationId: getNotificationRouting
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      responses:
        '200':
          description: 'Routing map + available options'
          content:
            application/json:
              schema:
                properties:
                  routing: { type: object, example: { story.completed: [in_app, email] }, additionalProperties: { type: array, items: { type: string } } }
                  available: { properties: { events: { type: array, items: { type: string } }, channels: { type: array, items: { type: string } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - Notification
      summary: 'Update notification event routing'
      description: 'Replaces the event -> channel[] routing map. Unknown events/channels are rejected. Accessible by workspace-admin and super roles.'
      operationId: putNotificationRouting
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - routing
              properties:
                routing:
                  type: object
                  example: { story.completed: [in_app, email], export.ready: [slack] }
                  additionalProperties: { type: array, items: { type: string } }
              type: object
      responses:
        '204':
          description: Saved
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/notifications/unread-count:
    get:
      tags:
        - Notification
      summary: 'Unread notification count'
      description: 'Returns the number of unread in-app notifications for the current user. Designed for polling.'
      operationId: getNotificationUnreadCount
      responses:
        '200':
          description: 'Unread count'
          content:
            application/json:
              schema:
                properties:
                  count: { type: integer, example: 3 }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/notification-channels':
    get:
      tags:
        - Notification
      summary: 'Get workspace notification channel config'
      description: 'Returns enabled status for each notification channel of the workspace. Secrets (webhook_url, bot_token) are never returned. Accessible by workspace-admin and super roles.'
      operationId: getWorkspaceNotificationChannels
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      responses:
        '200':
          description: 'Channel config (secrets omitted)'
          content:
            application/json:
              schema:
                properties:
                  slack: { properties: { enabled: { type: boolean }, configured: { description: 'true when webhook_url is set', type: boolean } }, type: object }
                  email: { properties: { enabled: { type: boolean } }, type: object }
                  telegram: { properties: { enabled: { type: boolean }, configured: { description: 'true when bot_token AND chat_id are set', type: boolean } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - Notification
      summary: 'Update workspace notification channel config'
      description: 'Saves workspace notification channel configuration. Secrets (webhook_url, bot_token) are write-only and never returned. Accessible by workspace-admin and super roles.'
      operationId: putWorkspaceNotificationChannels
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - slack
                - email
                - telegram
              properties:
                slack:
                  required: [enabled]
                  properties: { enabled: { type: boolean }, webhookUrl: { description: 'Omit to keep existing value', type: string, nullable: true } }
                  type: object
                email:
                  required: [enabled]
                  properties: { enabled: { type: boolean } }
                  type: object
                telegram:
                  required: [enabled]
                  properties: { enabled: { type: boolean }, botToken: { description: 'Omit/empty to keep existing value', type: string, nullable: true }, chatId: { description: 'Omit/empty to keep existing value', type: string, nullable: true } }
                  type: object
              type: object
      responses:
        '204':
          description: Saved
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/notifications:
    get:
      tags:
        - Notification
      summary: 'List in-app notifications'
      description: "Returns the current user's in-app notifications, newest first, scoped to their workspace."
      operationId: listNotifications
      parameters:
        -
          name: limit
          in: query
          description: 'Page size (max 100).'
          required: false
          schema:
            type: integer
            default: 20
        -
          name: offset
          in: query
          description: 'Row offset.'
          required: false
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: 'List of notifications with total row count for paging'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f }, eventType: { type: string, example: story.completed }, title: { type: string, example: 'Story "mobile-v2" completed' }, body: { type: string, example: 'AI translation for story "mobile-v2" has finished.' }, entityType: { type: string, example: story, nullable: true }, entityId: { type: string, example: '42', nullable: true }, isRead: { type: boolean, example: false }, readAt: { type: string, example: null, nullable: true }, createdAt: { type: string, example: '2026-07-03 10:00:00' }, initiatorName: { description: 'Full name of the user who triggered the notification; null when system/cron-triggered.', type: string, example: 'John Doe', nullable: true } }, type: object } }
                  total: { description: "Total row count of the caller's notifications (for pagination).", type: integer, example: 42 }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/notifications/read-all:
    patch:
      tags:
        - Notification
      summary: 'Mark all notifications as read'
      description: 'Marks all unread in-app notifications as read for the current user in their workspace.'
      operationId: markAllNotificationsRead
      responses:
        '204':
          description: 'All marked as read'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/notifications/{id}/read':
    patch:
      tags:
        - Notification
      summary: 'Mark notification as read'
      description: 'Marks a single notification as read. The notification must belong to the current user.'
      operationId: markNotificationRead
      parameters:
        -
          name: id
          in: path
          description: 'Notification UUID'
          required: true
          schema:
            type: string
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      responses:
        '204':
          description: 'Marked as read'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v1/auth/login:
    post:
      tags:
        - Auth
      summary: 'Start authentication flow'
      description: 'Starts the login flow. If two-factor authentication is enabled the response contains a temporary token and the request is accepted for further verification.'
      operationId: 4aab546f97fd8a1738041b7c635df144
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                  example: user@example.com
                password:
                  type: string
                  example: s3cr3t
              type: object
      responses:
        '202':
          description: 'Two-factor authentication required'
          content:
            application/json:
              schema:
                properties:
                  email: { type: string, format: email, example: user@example.com }
                  is_two_fa_active: { type: boolean, example: true }
                  has_used_qr: { type: boolean, example: false }
                  access_token: { properties: { token: { type: string, example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 }, expiresAt: { type: integer, example: 1707309187 } }, type: object }
                type: object
        '200':
          description: 'Authenticated successfully'
          content:
            application/json:
              schema:
                properties:
                  is_two_fa_active: { type: boolean, example: false }
                  access_token: { properties: { token: { type: string, example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 }, expiresAt: { type: integer, example: 1707309187 } }, type: object }
                  refresh_token: { properties: { token: { type: string, example: def50200b4 }, expiresAt: { type: integer, example: 1709911187 } }, type: object }
                type: object
        '400':
          description: 'Validation failed'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: 'Invalid email or password' }
                  errors: { type: object, example: { email: ['Email cannot be blank'], password: ['Password cannot be blank'] } }
                type: object
        '401':
          description: Unauthorized
        '404':
          description: 'User is blocked or not confirmed'
  '/api/v2/page-information/product/{productId}':
    get:
      tags:
        - PageInformation
      summary: 'Get detailed product page information'
      description: 'Returns product page data (including optional project type, category and buttons)'
      operationId: getProductPageInformation
      parameters:
        -
          name: productId
          in: path
          description: 'The product ID'
          required: true
          schema:
            type: string
            format: uuid
          example: 6f8a1d2c-1345-4a5c-8bde-3fef13a0c123
        -
          name: projectTypeId
          in: query
          description: 'Optional project type ID'
          required: false
          schema:
            type: integer
          example: 12
        -
          name: categoryId
          in: query
          description: 'Optional category ID'
          required: false
          schema:
            type: integer
          example: 4
      responses:
        '200':
          description: 'Product page information retrieved successfully'
          content:
            application/json:
              schema:
                type: object
              example:
                product:
                  id: 6f8a1d2c-1345-4a5c-8bde-3fef13a0c123
                  name: B2Core
                  iconName: b2core.svg
                  languageId: 1
                  languages: [1, 2]
                  platforms: [web]
                projectType:
                  id: 12
                  name: Broker
                  languageId: 1
                  lang: en
                  platform: web
                category:
                  id: 4
                  name: General
                  projectTypeId: 12
                buttons:
                  - CategoryCreateButton
                  - ProductCreateButton
                  - ProductUpdateButton
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/page-information/project/{projectId}':
    get:
      tags:
        - PageInformation
      summary: 'Get detailed project page information'
      description: 'Returns the full project page data (including language, category, story, ai usage and buttons)'
      operationId: getProjectPageInformation
      parameters:
        -
          name: projectId
          in: path
          description: 'The project ID'
          required: true
          schema:
            type: integer
          example: 76
        -
          name: storyId
          in: query
          description: 'Optional story ID'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: categoryId
          in: query
          description: 'Optional category ID'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: languageId
          in: query
          description: 'Optional language ID'
          required: false
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Project page information retrieved successfully'
          content:
            application/json:
              schema:
                type: object
              example:
                project:
                  id: 24
                  name: 'Main Project'
                category:
                  id: 2
                  name: Marketing
                language:
                  id: 1
                  lang: en
                  name: English
                  dir: ltr
                story:
                  id: 5
                  name: Intro
                projectType:
                  id: 7
                  name: 'Mobile App'
                  languageId: 1
                  lang: en
                aiUsage:
                  aiLanguages: [en, fr]
                  defaultLanguage: en
                  balance: 4.7
                  total: 9
                buttons:
                  - ProjectCategoryCreateButton
                  - ProjectSourceCreateButton
                  - ProjectSendToButton
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/page-information/story/{projectTypeId}':
    get:
      tags:
        - PageInformation
      summary: 'Get detailed story page information'
      description: 'Returns the full story page data (including project type, story, language, merge story, category, contexts info, ai usage and buttons)'
      operationId: getStoryPageInformation
      parameters:
        -
          name: projectTypeId
          in: path
          description: 'The project type ID'
          required: true
          schema:
            type: integer
          example: 7
        -
          name: storyId
          in: query
          description: 'Optional story ID'
          required: false
          schema:
            type: integer
          example: 5
        -
          name: mergeStoryId
          in: query
          description: 'Optional merge story ID'
          required: false
          schema:
            type: integer
          example: 10
        -
          name: categoryId
          in: query
          description: 'Optional category ID'
          required: false
          schema:
            type: integer
          example: 4
        -
          name: languageId
          in: query
          description: 'Optional language ID'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: contexts
          in: query
          description: 'If set to 1, includes context statistics for the project type'
          required: false
          schema:
            type: integer
            enum:
              - 0
              - 1
          example: 0
      responses:
        '200':
          description: 'Story page information retrieved successfully'
          content:
            application/json:
              schema:
                type: object
              example:
                projectType:
                  id: 7
                  name: 'Mobile App'
                  alias: mobile-app
                  languageId: 1
                  lang: en
                story:
                  id: 5
                  name: production
                language:
                  id: 1
                  lang: en
                  name: English
                  dir: ltr
                mergeStory:
                  id: 10
                  name: develop
                category:
                  id: 4
                  name: General
                contextsInfo:
                  allKeys: 500
                  keysWithContext: 120
                aiUsage:
                  aiLanguages: [en, fr, de]
                  defaultLanguage: 1
                  balance: 4.7
                  total: 10
                buttons:
                  - StoryCreateButton
                  - StoryCategoryCreateButton
                  - StorySourceCreateButton
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/event-queue:
    get:
      tags:
        - Queue
      summary: 'Get event queue'
      description: 'Lists PostgreSQL event queue rows for the admin section. Super-only (root / Owner / SuperAdmin).'
      operationId: getEventQueue
      parameters:
        -
          name: status
          in: query
          description: 'Queue status filter (case-insensitive). Unknown or empty values fall back to "dead".'
          required: false
          schema:
            type: string
            default: dead
            enum:
              - pending
              - processing
              - done
              - dead
        -
          name: limit
          in: query
          description: 'Page size, clamped to [10; 500].'
          required: false
          schema:
            type: integer
            default: 50
        -
          name: offset
          in: query
          description: 'Row offset.'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: sort
          in: query
          description: 'Sort field, optionally prefixed with "-" for descending. Unknown values fall back to "-createdAt" (newest first). An "id DESC" tiebreaker is always applied.'
          required: false
          schema:
            type: string
            default: '-createdAt'
            enum:
              - createdAt
              - '-createdAt'
              - attempts
              - '-attempts'
              - type
              - '-type'
              - status
              - '-status'
              - processedAt
              - '-processedAt'
              - availableAt
              - '-availableAt'
      responses:
        '200':
          description: 'Successful response with a list of event queue rows'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f }, type: { type: string, example: ai.batch_translate }, status: { type: string, enum: [pending, processing, done, dead], example: dead }, attempts: { type: integer, example: 5 }, maxAttempts: { type: integer, example: 5 }, dedupKey: { type: string, example: 'ai.batch_translate:42', nullable: true }, error: { type: string, example: 'AI provider timeout', nullable: true }, availableAt: { type: string, example: '2026-07-02 10:00:00' }, lockedUntil: { type: string, example: null, nullable: true }, createdAt: { type: string, example: '2026-07-02 09:59:00' }, processedAt: { type: string, example: null, nullable: true }, payload: { type: object, example: {  } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 123 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/event-queue/{id}/retry':
    post:
      tags:
        - Queue
      summary: 'Retry dead event'
      description: 'Requeues a dead event queue row: status back to pending, attempts reset to 0, error and locks cleared, available immediately. Anything not dead is refused — a pending row is already queued and a processing row is owned by a worker. Super-only (root / Owner / SuperAdmin).'
      operationId: retryEventQueueEvent
      parameters:
        -
          name: id
          in: path
          description: 'Event queue row id'
          required: true
          schema:
            type: string
            format: uuid
          example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f
      responses:
        '200':
          description: 'Event requeued'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 0197f3c1-1b2c-7d3e-8f40-0a1b2c3d4e5f }
                  status: { type: string, enum: [pending], example: pending }
                type: object
        '400':
          description: 'Malformed id, or the event is not dead'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { id: { type: string, example: 'Only dead events can be retried, this one is processing.' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not found'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { id: { type: string, example: 'Event not found!' } }, type: object }
                type: object
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/stories:
    post:
      tags:
        - Story
      summary: 'Create story'
      description: 'Create a new story'
      operationId: createStory
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeId:
                  type: integer
                  example: 1
                name:
                  type: string
                  example: my-story
              type: object
      responses:
        '200':
          description: 'Successful response with created story'
          content:
            application/json:
              schema:
                properties:
                  id: { type: integer, example: 1 }
                  name: { type: string, example: my-story }
                  projectType: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: default }, alias: { type: string, example: default } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/by-platform/{id}':
    get:
      tags:
        - Story
      summary: 'Get stories by project and category name'
      description: 'Returns a list of stories filtered by category name and other parameters.'
      operationId: getProjectCategoryStories
      parameters:
        -
          name: id
          in: path
          description: 'platform id'
          required: true
          schema:
            type: integer
            example: 76
        -
          name: offset
          in: query
          required: false
          schema:
            type: integer
            example: 0
        -
          name: limit
          in: query
          required: false
          schema:
            type: integer
            example: 20
        -
          name: storyName
          in: query
          required: false
          schema:
            type: string
            example: production
        -
          name: categoryName
          in: query
          required: false
          schema:
            type: string
            example: default
      responses:
        '200':
          description: 'List of stories'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: production }, createdAt: { properties: { date: { type: string, example: '2023-05-16 21:36:35.000000' }, timezoneType: { type: integer, example: 1 }, timezone: { type: string, example: '+04:00' } }, type: object }, projectType: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: b2core }, alias: { type: string, example: b2core_alias }, createdAt: { properties: { date: { type: string, example: '2023-05-16 21:36:30.000000' }, timezoneType: { type: integer, example: 1 }, timezone: { type: string, example: '+04:00' } }, type: object }, updatedAt: { properties: { date: { type: string, example: '2023-06-01 12:00:00.000000' }, timezoneType: { type: integer, example: 1 }, timezone: { type: string, example: '+04:00' } }, type: object, nullable: true } }, type: object } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/by-project-type/{id}':
    get:
      tags:
        - Story
      summary: 'Get all stories by project type'
      description: 'Fetches all stories filtered by project type, with optional parameters for pagination, sorting, and filtering by name or created date.'
      operationId: getStoriesByProjectType
      parameters:
        -
          name: id
          in: path
          description: 'The project type ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of stories to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: sort
          in: query
          description: 'Sort by field name. Available options: "name", "-name", "createdAt", "-createdAt", "changerEmail", "-changerEmail"'
          required: false
          schema:
            type: string
          example: '-createdAt'
        -
          name: name
          in: query
          description: 'Filter stories by name'
          required: false
          schema:
            type: string
          example: 'My Story Name'
        -
          name: createdAt
          in: query
          description: 'Filter stories by creation date (in yyyy-mm-dd:yyyy-mm-dd format)'
          required: false
          schema:
            type: string
            format: date
          example: '2023-10-10:2024-10-10'
      responses:
        '200':
          description: 'Successful response with a list of stories'
          content:
            application/json:
              schema:
                properties:
                  stories: { type: array, items: { properties: { id: { type: integer, example: 101 }, projectTypeId: { type: integer, example: 1 }, name: { type: string, example: 'Story Title' }, createdAt: { type: string, format: date, example: '2023-10-15' }, changerEmail: { type: string, example: user@example.com }, allKeysCount: { type: integer, example: 5 }, actions: { type: array, items: { type: string, enum: [StoryUpdate, StoryDelete], example: StoryUpdate } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/{id}/available':
    get:
      tags:
        - Story
      summary: 'Get available stories for merge'
      description: 'Returns stories of the same project type as the given story, excluding the story itself. Used for merge modal.'
      operationId: getAvailableStories
      parameters:
        -
          name: id
          in: path
          description: 'Story ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter by story name'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'List of available stories'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: production }, createdAt: { type: string, format: date-time }, projectType: { properties: { id: { type: integer }, name: { type: string }, alias: { type: string }, createdAt: { type: string, format: date-time }, updatedAt: { type: string, format: date-time } }, type: object } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 42 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/by-project/{id}':
    get:
      tags:
        - Story
      summary: 'Get stories by project'
      description: 'Returns all available (non-in-progress) stories belonging to the specified project, with optional filtering by story name and category name.'
      operationId: getStoriesByProject
      parameters:
        -
          name: id
          in: path
          description: 'Project ID'
          required: true
          schema:
            type: integer
            example: 1
        -
          name: name
          in: query
          description: 'Filter stories by name'
          required: false
          schema:
            type: string
          example: production
        -
          name: categoryName
          in: query
          description: 'Filter stories by category name'
          required: false
          schema:
            type: string
          example: default
      responses:
        '200':
          description: 'Successful response with a list of available stories'
          content:
            application/json:
              schema:
                properties:
                  stories: { type: array, items: { properties: { id: { type: integer, example: 101 }, name: { type: string, example: production }, createdAt: { type: string, example: '2023-10-15 12:00:00' }, projectTypeId: { type: integer, example: 5 }, projectTypeName: { type: string, example: b2core } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/{id}':
    delete:
      tags:
        - Story
      summary: 'Delete story'
      description: 'Delete story'
      operationId: deleteStory
      parameters:
        -
          name: id
          in: path
          description: 'The story Id'
          required: true
          schema:
            type: integer
          example: 1
      responses:
        '204':
          description: 'Story deleted successfully (no content)'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    patch:
      tags:
        - Story
      summary: 'Update story'
      description: 'Update a story'
      operationId: updateStory
      parameters:
        -
          name: id
          in: path
          description: 'The story Id'
          required: true
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                name:
                  type: string
                  example: my-story
              type: object
      responses:
        '200':
          description: 'Successful response with updated story'
          content:
            application/json:
              schema:
                properties:
                  id: { type: integer, example: 1 }
                  name: { type: string, example: my-story }
                  projectType: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: default }, alias: { type: string, example: default } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/products/auto-translate:
    post:
      tags:
        - Tenant/Product
      summary: 'Trigger auto-translation for new languages on selected platforms'
      description: "Enqueues AI auto-translation of the given languages for the chosen platforms. One event is fired per (platform x language), skipping each platform's own base language."
      operationId: autoTranslateNewLanguages
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - languageIds
                - projectTypeIds
              properties:
                languageIds:
                  type: array
                  items: { type: integer }
                  example: [2, 3, 4]
                projectTypeIds:
                  type: array
                  items: { type: integer }
                  example: [10, 11]
              type: object
      responses:
        '202':
          description: 'Auto-translation enqueued'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: Accepted }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{productId}/buckets':
    get:
      tags:
        - Tenant/Buckets
      summary: 'List buckets of a product'
      description: 'Returns the buckets (platforms) that belong to a product.'
      operationId: getProductBuckets
      parameters:
        -
          name: productId
          in: path
          description: 'UUID of the product'
          required: true
          schema:
            type: string
            format: uuid
          example: 8ebfac95-50f4-422b-a257-6c6143b999c4
      responses:
        '200':
          description: 'Product buckets'
          content:
            application/json:
              schema:
                properties:
                  platforms: { type: object, additionalProperties: { properties: { projectTypeId: { type: integer, example: 1 }, projectTypeName: { type: string, example: b2core-default }, platform: { type: string, example: default }, createdAt: { type: string, format: date-time } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Product not found'
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Tenant/Buckets
      summary: 'Create a bucket inside a product'
      description: 'Creates a new bucket (platform) inside a product. Bucket name must match [a-z0-9][a-z0-9_-]{0,49} and be unique within the product.'
      operationId: createProductBucket
      parameters:
        -
          name: productId
          in: path
          description: 'UUID of the product'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  type: string
                  example: mobile
                context:
                  type: string
                  example: null
                  nullable: true
              type: object
      responses:
        '201':
          description: 'Bucket created'
          content:
            application/json:
              schema:
                properties:
                  projectTypeId: { type: integer, example: 42 }
                  projectTypeName: { type: string, example: b2core-mobile }
                  platform: { type: string, example: mobile }
                  createdAt: { type: string, format: date-time }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Product not found'
        '409':
          description: 'Bucket name already exists for product, or a subscription plan limit was reached (`planLimit`)'
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/products:
    get:
      tags:
        - Tenant/Product
      summary: 'Get products for user'
      description: 'Fetches all existing products, with optional parameters for pagination'
      operationId: getProducts
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of products to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: permission
          in: query
          description: 'Permission name'
          required: false
          schema:
            type: string
          example: Creator
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: string, example: 092a4feb-579c-40f9-aea6-3fc7c5438cf6 }, name: { type: string, example: b2binpay }, iconName: { type: string, example: b2binpay-icon, nullable: true }, platforms: { type: array, items: { type: string, example: web } }, languages: { type: array, items: { type: integer, example: 1 } }, languageId: { type: integer, example: 1 }, storyCount: { type: integer, example: 33 }, availablePlatforms: { type: array, items: { type: string, example: web } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Tenant/Product
      summary: 'Create a product with platforms and languages'
      operationId: createProduct
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
                - iconName
                - languages
                - defaultLanguageId
              properties:
                name:
                  type: string
                  example: B2Core
                iconName:
                  type: string
                  example: b2core-icon
                languages:
                  type: array
                  items: { type: integer }
                  example: [1, 2, 3, 4, 5]
                defaultLanguageId:
                  type: integer
                  example: 1
                platforms:
                  type: array
                  items: { required: [type], properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object }
              type: object
      responses:
        '200':
          description: 'Product created successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }
                  name: { type: string, example: B2Core }
                  iconName: { type: string, example: b2core-icon }
                  languages: { type: array, items: { type: integer }, example: [1, 2, 3, 4, 5] }
                  defaultLanguageId: { type: integer, example: 1 }
                  platforms: { type: array, items: { properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{uid}/robot-tokens':
    get:
      tags:
        - 'Product Robot Tokens'
      summary: 'List product robot API tokens'
      description: 'Returns the list of API tokens for the robot user attached to the product.'
      operationId: getProductRobotTokens
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successful response with list of robot API tokens.'
          content:
            application/json:
              schema:
                type: array
                items:
                  properties: { id: { type: string, format: uuid }, name: { type: string }, createdAt: { type: string, format: date-time }, expiresAt: { type: string, format: date-time }, isActive: { type: boolean }, actions: { type: array, items: { type: string } } }
                  type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Product or robot user not found'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - 'Product Robot Tokens'
      summary: 'Create product robot API token'
      description: 'Creates a new API token for the robot user attached to the product. Maximum 2 active tokens allowed.'
      operationId: createProductRobotToken
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  type: string
                  example: 'CI integration'
                expiresInHours:
                  type: integer
                  maximum: 24
                  minimum: 1
                  nullable: true
                expiresAt:
                  type: string
                  format: date-time
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Created successfully. Token value is returned only once.'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid }
                  name: { type: string }
                  createdAt: { type: string, format: date-time }
                  expiresAt: { type: string, format: date-time }
                  token: { type: string }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Product or robot user not found'
        '409':
          description: 'Maximum active tokens reached'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/tenants/by-robot:
    post:
      tags:
        - Tenant
      summary: 'Create or update tenant by robot'
      description: 'Creates a new tenant or updates an existing one. Returns tenantId only.'
      operationId: createOrUpdateTenantByRobot
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - productId
                - platforms
                - name
                - languages
              properties:
                productId:
                  type: string
                  format: uuid
                  example: 8ebfac95-50f4-422b-a257-6c6143b999c4
                name:
                  type: string
                  example: test-tenant
                platforms:
                  type: array
                  items: { type: string, example: web }
                languages:
                  type: array
                  items: { type: integer, example: 1 }
              type: object
      responses:
        '200':
          description: 'Tenant created or updated'
          content:
            application/json:
              schema:
                properties:
                  tenantId: { type: string, format: uuid, example: b9d1b72d-8351-4a0e-8f3e-d81d52f14ea9 }
                type: object
        '400':
          description: 'Bad Request'
        '401':
          description: Unauthorized
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/tenants:
    get:
      tags:
        - Tenant
      summary: 'Get all tenants'
      description: 'Fetches all tenants, with optional parameters for pagination and search'
      operationId: getTenants
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of tenants to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: search
          in: query
          description: 'Optional search term to filter tenants'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Successful response with a list of tenants'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string, format: uuid, example: ab51808b-43f3-4874-b5ed-2417f62b5b2c }, name: { type: string, example: saas.b2broker }, languages: { type: array, items: { type: integer, example: 1 } }, productId: { type: string, format: uuid, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }, allSourcesCount: { type: integer, example: 465 }, languagesCount: { type: integer, example: 1 } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 1 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Tenant
      summary: 'Create a new tenant'
      description: 'Creates a tenant with provided name, platforms, languages and product ID.'
      operationId: createTenant
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                productId:
                  type: string
                  format: uuid
                  example: 2456e544-60c1-4876-b5d9-531d2cbd7bfa
                name:
                  type: string
                  example: test2
                platforms:
                  type: array
                  items: { type: string, example: web }
                languages:
                  type: array
                  items: { type: integer, example: 1 }
                orderedLanguages:
                  description: 'Optional ordered list of language IDs for display ordering'
                  type: array
                  items: { type: integer, example: 1 }
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Tenant created successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe }
                  name: { type: string, example: test2 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  productId: { type: string, format: uuid, example: 2456e544-60c1-4876-b5d9-531d2cbd7bfa }
                  changerId: { type: integer, example: 5 }
                  createdAt: { type: string, example: '2025-05-03 08:50:11', nullable: false }
                  updatedAt: { type: string, example: null, nullable: true }
                  deletedAt: { type: string, example: null, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{productId}/buckets/{id}':
    delete:
      tags:
        - Tenant/Buckets
      summary: 'Delete a bucket from a product'
      description: 'Deletes a bucket (platform) from a product. Fails with 409 if the bucket still contains translation keys.'
      operationId: deleteProductBucket
      parameters:
        -
          name: productId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        -
          name: id
          in: path
          description: 'Bucket (project_type) numeric id'
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Bucket deleted'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Bucket not found in product'
        '409':
          description: 'Bucket has keys'
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{id}':
    get:
      tags:
        - Tenant/Product
      summary: 'Get product details with platforms and languages'
      operationId: getProduct
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 7c6c2c68-2def-421a-b4d0-107351f13049
      responses:
        '200':
          description: 'Product data'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }
                  name: { type: string, example: B2Core }
                  iconName: { type: string, example: b2core-icon, nullable: true }
                  languages: { type: array, items: { type: integer }, example: [1, 2, 3, 4, 5] }
                  defaultLanguageId: { type: integer, example: 1 }
                  platforms: { type: array, items: { properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Product not found'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - Tenant/Product
      summary: 'Delete a product with full cascade'
      description: 'Hard deletes a product and all its dependencies (project types, tenants, projects, sources, translations, stories, etc.) in a single transaction.'
      operationId: deleteProduct
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the product to delete'
          required: true
          schema:
            type: string
            format: uuid
          example: 7c6c2c68-2def-421a-b4d0-107351f13049
      responses:
        '200':
          description: 'Product successfully deleted'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{uid}/robot-tokens/{id}':
    delete:
      tags:
        - 'Product Robot Tokens'
      summary: 'Delete product robot API token'
      description: 'Deletes a robot API token.'
      operationId: deleteProductRobotToken
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
        -
          name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Deleted successfully.'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not found'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{id}':
    get:
      tags:
        - Tenant
      summary: 'Get one tenant'
      description: 'Fetches one tenant.'
      operationId: getOneTenant
      parameters:
        -
          name: id
          in: path
          description: 'ID of the project'
          required: true
          schema:
            type: string
            format: uuid
          example: c9c43019-b7b4-4906-870c-247324df4df5
      responses:
        '200':
          description: 'Tenant object'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 8d6c398d-5a55-4d7d-a7aa-eaefcde6f9f6 }
                  name: { type: string, example: test2 }
                  languages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English }, lang: { type: string, example: en }, dir: { type: string, example: ltr } }, type: object } }
                  platforms: { properties: { productPlatforms: { type: array, items: { type: string, example: web } }, tenantPlatforms: { properties: { id: { type: integer, example: 1000 }, name: { type: string, example: test2 }, uuid: { type: string, format: uuid, example: b6a892dd-eaed-47e9-9096-8c9d3d374e4b }, uid: { type: string, format: uuid, example: 312c8979-e58e-4c0d-baf9-79cac6ac050e }, tenantId: { type: string, format: uuid, example: 8d6c398d-5a55-4d7d-a7aa-eaefcde6f9f6 }, platform: { type: string, example: ios }, changerId: { type: integer, example: 5 }, projectTypeId: { type: integer, example: 86 }, createdAt: { type: string, example: '2025-05-30 16:29:55+04' }, deletedAt: { type: string, example: null, nullable: true } }, type: object } }, type: object }
                  productId: { type: string, format: uuid, example: 8ebfac95-50f4-422b-a257-6c6143b999c4 }
                  changerId: { type: integer, example: 5 }
                  createdAt: { type: string, example: '2025-05-30 16:29:55' }
                  updatedAt: { type: string, example: null, nullable: true }
                  deletedAt: { type: string, example: null, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - Tenant
      summary: 'Update an existing tenant'
      description: "Updates a tenant's name and language list."
      operationId: updateTenant
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the tenant to update'
          required: true
          schema:
            type: string
            format: uuid
          example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                name:
                  type: string
                  example: UpdatedTenantName
                platforms:
                  type: array
                  items: { type: string, example: web }
                languages:
                  type: array
                  items: { type: integer, example: 1 }
                orderedLanguages:
                  description: 'Optional ordered list of language IDs for display ordering'
                  type: array
                  items: { type: integer, example: 1 }
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Tenant updated successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe }
                  name: { type: string, example: test2 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  productId: { type: string, format: uuid, example: 2456e544-60c1-4876-b5d9-531d2cbd7bfa }
                  changerId: { type: integer, example: 5 }
                  createdAt: { type: string, example: '2025-05-03 08:50:11', nullable: false }
                  updatedAt: { type: string, example: null, nullable: true }
                  deletedAt: { type: string, example: null, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - Tenant
      summary: 'Delete a tenant'
      description: 'Deletes a tenant by ID. This is a soft delete if supported.'
      operationId: deleteTenant
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the tenant to delete'
          required: true
          schema:
            type: string
            format: uuid
          example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe
      responses:
        '200':
          description: 'Tenant successfully deleted'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/available-by-ai-translator/{id}':
    get:
      tags:
        - Tenant/Product
      summary: 'Get all available products by ai translator'
      description: 'Fetches all available products by ai translator.'
      operationId: getAllAiAvailableProductsAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The Ai Translator Id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter projects by name'
          required: false
          schema:
            type: string
          example: 'My Project Name'
        -
          name: projectTypeId
          in: query
          description: 'Filter projects by project type ID'
          required: false
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful response with a list of projects'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: b2binpay }, languageId: { type: integer, example: 1 }, platforms: { type: array, items: { type: string, example: web } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/available-by-ai-translator/{id}':
    get:
      tags:
        - Tenant
      summary: 'Get all available tenants by ai translator'
      description: 'Fetches all available tenants by ai translator.'
      operationId: getAllAiAvailableTenantsAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The Ai Translator Id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter tenant by name'
          required: false
          schema:
            type: string
          example: 'My tenant name'
        -
          name: productId
          in: query
          description: 'Filter tenants by product ID'
          required: false
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
      responses:
        '200':
          description: 'Successful response with a list of tenants'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string, format: uuid, example: ab51808b-43f3-4874-b5ed-2417f62b5b2c }, name: { type: string, example: saas.b2broker }, productId: { type: string, format: uuid, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }, productName: { type: string, example: b2broker } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/by-ai-translator/{id}':
    get:
      tags:
        - Tenant/Product
      summary: 'Get all products by ai translator'
      description: 'Fetches all products by ai translator.'
      operationId: getAllAiProductsAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The Ai Translator Id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: date
          in: query
          description: Date
          required: true
          schema:
            type: string
          example: '2025-03-11'
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter by name'
          required: false
          schema:
            type: string
          example: 'My Project Name'
        -
          name: productId
          in: query
          description: 'Filter by product ID'
          required: false
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: status
          in: query
          description: 'Filter by status(1-Active,2-Expired,3-Deactivated)'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: sort
          in: query
          description: 'Filter by sort(-lastUpdate,lastUpdate,-price,price)'
          required: false
          schema:
            type: string
          example: '-price'
      responses:
        '200':
          description: 'Successful response with a list of projects'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: b2binpay }, lastUpdate: { type: string, example: '2025-02-26 14:27:25' }, price: { type: number, format: float, example: 5.11 }, status: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: Active } }, type: object }, actions: { type: array, items: { type: string, example: AiProductUpdate } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/by-ai-translator/{id}':
    get:
      tags:
        - Tenant
      summary: 'Get all tenants by ai translator'
      description: 'Fetches all tenants by ai translator.'
      operationId: getAllAiTenantsAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The Ai Translator Id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: date
          in: query
          description: Date
          required: true
          schema:
            type: string
          example: '2025-03-11'
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter projects by name'
          required: false
          schema:
            type: string
          example: 'My Project Name'
        -
          name: productId
          in: query
          description: 'Filter projects by project type ID'
          required: false
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: status
          in: query
          description: 'Filter projects by status(1-Active,2-Expired,3-Deactivated)'
          required: false
          schema:
            type: integer
          example: 1
        -
          name: sort
          in: query
          description: 'Filter projects by sort(-lastUpdate,lastUpdate,-price,price)'
          required: false
          schema:
            type: string
          example: '-price'
      responses:
        '200':
          description: 'Successful response with a list of tenants'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string, format: uuid, example: ab51808b-43f3-4874-b5ed-2417f62b5b2c }, name: { type: string, example: saas.b2broker }, productId: { type: string, format: uuid, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }, lastUpdate: { type: string, example: '2025-02-26 14:27:25' }, price: { type: number, format: float, example: 5.11 }, status: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: Active } }, type: object }, actions: { type: array, items: { type: string, example: AiProjectUpdate } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/available-permissions-by-user/{id}':
    get:
      tags:
        - Tenant/Product
      summary: 'Get available product permissions by user'
      description: 'Fetches products with their buckets where the target user is missing project_type permissions. Each product includes a list of buckets (project_types) where the user has no permissions yet, with the available permissions the caller can grant.'
      operationId: getAvailableProductPermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The target user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of products to return'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: productId
          in: query
          description: 'Filter by a specific product UUID'
          required: false
          schema:
            type: string
            format: uuid
          example: 01936f1a-5b5a-7000-8000-000000000001
      responses:
        '200':
          description: 'List of products with missing buckets and grantable permissions'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: string, format: uuid }, name: { type: string, example: b2core }, buckets: { type: array, items: { properties: { projectTypeId: { type: integer, example: 3 }, name: { type: string, example: b2core-web }, alias: { type: string, example: b2core-web_alias }, platform: { type: string, example: web }, availablePermissions: { type: array, items: { type: string, example: Viewer } } }, type: object } }, actions: { type: array, items: { type: string, example: ProjectTypeUserDelete } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 5 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/projects/available-permissions-by-user/{id}':
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get all available project permissions by user'
      description: 'Fetches all available project permissions for a user with optional filters.'
      operationId: getAllAvailableProjectPermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter projects by name'
          required: false
          schema:
            type: string
          example: 'My Project Name'
        -
          name: permission
          in: query
          description: 'Filter projects by specific permission'
          required: false
          schema:
            type: string
          example: Editor
        -
          name: projectTypeId
          in: query
          description: 'Filter projects by project type ID'
          required: false
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful response with a list of projects and their permissions'
          content:
            application/json:
              schema:
                properties:
                  projects: { type: array, items: { properties: { id: { type: integer, example: 248 }, name: { type: string, example: b2copy }, projectTypeId: { type: integer, example: 1 }, projectTypeName: { type: string, example: b2copy }, availablePermissions: { type: array, items: { type: string, example: Viewer } }, actions: { type: array, items: { type: string, example: ProjectUserDelete } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/project-types/available-permissions-by-user/{id}':
    get:
      tags:
        - 'Tenant/Project Type'
      summary: 'Get available project types with permissions by user'
      description: "Fetches all available project types with permissions for a specified user, \n            with optional parameters for pagination, project type filtering, and permission filtering."
      operationId: getAvailableProjectTypePermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of project types to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: permission
          in: query
          description: 'Filter by a specific permission role'
          required: false
          schema:
            type: string
          example: Viewer
        -
          name: projectTypeId
          in: query
          description: 'Filter by specific project type ID'
          required: false
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful response with a list of project types'
          content:
            application/json:
              schema:
                properties:
                  projectTypes: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: b2binpay }, alias: { type: string, example: b2binpay_alias }, availablePermissions: { type: array, items: { type: string, example: Viewer } }, actions: { type: array, items: { type: string, example: ProjectTypeUserDelete } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/available-permissions-by-user/{id}':
    get:
      tags:
        - Tenant/Tenant
      summary: 'Get available tenant permissions by user'
      description: 'Fetches tenants with their projects where the target user is missing project permissions. Each tenant includes a list of projects (tenant × bucket) where the user has no permissions yet, with the available permissions the caller can grant.'
      operationId: getAvailableTenantPermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The target user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of tenants to return'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: tenantId
          in: query
          description: 'Filter by a specific tenant UUID'
          required: false
          schema:
            type: string
            format: uuid
          example: 01936f1a-5b5a-7000-8000-000000000001
        -
          name: search
          in: query
          description: 'Case-insensitive substring match on tenant.name'
          required: false
          schema:
            type: string
          example: demo
      responses:
        '200':
          description: 'List of tenants with missing projects and grantable permissions'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { tenantId: { type: string, format: uuid }, tenantName: { type: string, example: DEMO }, productName: { type: string, example: b2core }, projects: { type: array, items: { properties: { id: { type: integer, example: 12 }, name: { type: string, example: DEMO_web }, platform: { type: string, example: web }, projectTypeId: { type: integer, example: 3 }, projectTypeName: { type: string, example: b2core-web }, availablePermissions: { type: array, items: { type: string, example: Viewer } } }, type: object } }, actions: { type: array, items: { type: string, example: ProjectUserDelete } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 5 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/editor-platforms/by-product/{productId}':
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get editor platforms(platforms in stories module) for a specific product'
      description: 'Returns a list of all editor platforms(platforms in stories module) by product.'
      operationId: getEditorPlatformsByProduct
      parameters:
        -
          name: productId
          in: path
          description: 'UUID of the product'
          required: true
          schema:
            type: string
            format: uuid
          example: 2456e544-60c1-4876-b5d9-531d2cbd7bfa
      responses:
        '200':
          description: 'Editor platform data by product'
          content:
            application/json:
              schema:
                properties:
                  platforms: { description: 'Platforms the user has editor permissions for', type: object, example: { web: { projectTypeId: 1, projectTypeName: b2core, platform: web, createdAt: '2023-05-16T21:36:30+04:00' } }, additionalProperties: { properties: { projectTypeId: { type: integer, example: 1 }, projectTypeName: { type: string, example: b2core }, platform: { type: string, example: web }, createdAt: { type: string, format: date-time, example: '2023-05-16T21:36:30+04:00' } }, type: object } }
                  productPlatforms: { description: 'All platforms that belong to this product', type: object, example: { web: { projectTypeId: 1, projectTypeName: b2core, platform: web, createdAt: '2023-05-16T21:36:30+04:00' }, ios: { projectTypeId: 2, projectTypeName: b2core-ios, platform: ios, createdAt: '2023-05-16T21:36:30+04:00' }, android: { projectTypeId: 3, projectTypeName: b2core-android, platform: android, createdAt: '2023-05-16T21:36:30+04:00' } }, additionalProperties: { properties: { projectTypeId: { type: integer, example: 1 }, projectTypeName: { type: string, example: b2core }, platform: { type: string, example: web }, createdAt: { type: string, format: date-time, example: '2023-05-16T21:36:30+04:00' } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/editor-products:
    get:
      tags:
        - Tenant/Product
      summary: 'Get editor products(products in stories module) for user'
      description: 'Fetches all existing products(products in stories module), with optional parameters for pagination'
      operationId: getEditorProducts
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of products to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: string, example: 092a4feb-579c-40f9-aea6-3fc7c5438cf6 }, name: { type: string, example: b2binpay }, languageId: { type: integer, example: 1 }, platforms: { type: array, items: { type: string, example: web } }, storyCount: { type: integer, example: 2 } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/platforms/by-product/{productId}':
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get platforms available for a specific product'
      description: 'Returns a list of available platforms mapped by platform key for a given product.'
      operationId: getPlatformsByProduct
      parameters:
        -
          name: productId
          in: path
          description: 'UUID of the product'
          required: true
          schema:
            type: string
            format: uuid
          example: 8ebfac95-50f4-422b-a257-6c6143b999c4
      responses:
        '200':
          description: 'Platform data by product'
          content:
            application/json:
              schema:
                properties:
                  platforms: { type: object, example: { web: { projectTypeId: 1, projectTypeName: b2core, platform: web, createdAt: '2023-05-16T21:36:30+04:00' }, ios: { projectTypeId: 2, projectTypeName: b2core-ios, platform: ios, createdAt: '2023-05-16T21:36:30+04:00' } }, additionalProperties: { properties: { projectTypeId: { type: integer, example: 1 }, projectTypeName: { type: string, example: b2core }, platform: { type: string, example: web }, createdAt: { type: string, format: date-time, example: '2023-05-16T21:36:30+04:00' } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/platforms/by-tenant/{tenantId}':
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get platforms assigned to a specific tenant'
      description: 'Returns available product platforms and actual tenant platform details by tenant ID.'
      operationId: getPlatformsByTenant
      parameters:
        -
          name: tenantId
          in: path
          description: 'UUID of the tenant'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      responses:
        '200':
          description: 'Tenant platform data'
          content:
            application/json:
              schema:
                properties:
                  productPlatforms: { type: array, items: { type: string, example: [web, ios] } }
                  availableProductPlatforms: { type: array, items: { type: string, example: web } }
                  availableTenantPlatforms: { type: object, example: { web: { id: 923, name: saas.b2broker, uuid: 3fa85f64-5717-4562-b3fc-2c963f66afa6, uid: 3fa85f64-5717-4562-b3fc-2c963f66afa6, tenantId: 3fa85f64-5717-4562-b3fc-2c963f66afa6, platform: web, changerId: 57, projectTypeId: 1, createdAt: '2024-12-19T20:33:39.559646+04:00', deletedAt: null }, ios: { id: 924, name: saas.ios, uuid: 3fa85f64-5717-4562-b3fc-2c963f66afa7, uid: 3fa85f64-5717-4562-b3fc-2c963f66afa7, tenantId: 3fa85f64-5717-4562-b3fc-2c963f66afa7, platform: ios, changerId: 58, projectTypeId: 2, createdAt: '2024-12-19T21:00:00.000000+04:00', deletedAt: null } }, additionalProperties: { properties: { id: { type: integer, example: 923 }, name: { type: string, example: saas.b2broker }, uuid: { type: string, format: uuid }, uid: { type: string, format: uuid }, tenantId: { type: string, format: uuid }, platform: { type: string, example: web }, changerId: { type: integer }, projectTypeId: { type: integer }, createdAt: { type: string, format: date-time }, deletedAt: { type: string, example: null, nullable: true } }, type: object } }
                  tenantPlatforms: { type: object, example: { web: { id: 923, name: saas.b2broker, uuid: 3fa85f64-5717-4562-b3fc-2c963f66afa6, tenantId: 3fa85f64-5717-4562-b3fc-2c963f66afa6, platform: web, changerId: 57, projectTypeId: 1, createdAt: '2024-12-19T20:33:39.559646+04:00', deletedAt: null }, ios: { id: 924, name: saas.ios, uuid: 3fa85f64-5717-4562-b3fc-2c963f66afa7, tenantId: 3fa85f64-5717-4562-b3fc-2c963f66afa7, platform: ios, changerId: 58, projectTypeId: 2, createdAt: '2024-12-19T21:00:00.000000+04:00', deletedAt: null } }, additionalProperties: { properties: { id: { type: integer, example: 923 }, name: { type: string, example: saas.b2broker }, uuid: { type: string, format: uuid }, tenantId: { type: string, format: uuid }, platform: { type: string, example: web }, changerId: { type: integer }, projectTypeId: { type: integer }, createdAt: { type: string, format: date-time }, deletedAt: { type: string, example: null, nullable: true } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/by-ai-translator/{id}/all':
    get:
      tags:
        - Tenant/Product
      summary: 'Get products with for ai'
      description: 'Fetches all existing products, with optional parameters for pagination'
      operationId: getProductsByAi
      parameters:
        -
          name: id
          in: path
          description: 'The Ai Translator Id'
          required: true
          schema:
            type: string
          example: d3cb3f65-5b8b-4620-8480-5165621e1112
        -
          name: limit
          in: query
          description: 'Maximum number of project types to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  products: { type: array, items: { properties: { id: { type: string, example: d3cb3f65-5b8b-4620-8480-5165621e1112 }, name: { type: string, example: b2binpay } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/products/all:
    get:
      tags:
        - Tenant/Product
      summary: 'List all products with platforms and languages'
      operationId: getAllProductsFull
      responses:
        '200':
          description: 'List of products'
          content:
            application/json:
              schema:
                type: array
                items:
                  properties: { id: { type: string, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }, name: { type: string, example: B2Core }, iconName: { type: string, example: b2core-icon, nullable: true }, languages: { type: array, items: { type: integer }, example: [1, 2, 3, 4, 5] }, defaultLanguageId: { type: integer, example: 1 }, platforms: { type: array, items: { properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object } } }
                  type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  '/api/v2/projects/permissions-by-user/{id}':
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get all project permissions by user'
      description: 'Fetches all project permissions for a user with optional filters.'
      operationId: getAllProjectPermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of projects to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: name
          in: query
          description: 'Filter projects by name'
          required: false
          schema:
            type: string
          example: 'My Project Name'
        -
          name: permissions
          in: query
          description: 'Filter projects by specific permissions'
          required: false
          schema:
            type: string
            example: '["Deleter", "Viewer", "Editor"]'
        -
          name: projectTypeId
          in: query
          description: 'Filter projects by project type ID'
          required: false
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful response with a list of projects and their permissions'
          content:
            application/json:
              schema:
                properties:
                  projects: { type: array, items: { properties: { id: { description: 'Project ID', type: integer, example: 26 }, name: { description: 'Project name', type: string, example: DEMO }, hasEmptyPermissions: { description: 'Indicates whether the project has empty permissions', type: boolean, example: false }, projectTypeId: { description: 'The ID of the project type', type: integer, example: 2 }, projectPermissions: { description: 'Permissions available for the project', type: array, items: { type: string, enum: [Viewer, Editor, Deleter] }, example: [Viewer, Editor, Deleter] }, projectTypePermissions: { description: 'Permissions based on the project type', type: array, items: { type: string, enum: [Viewer, Editor, Deleter] }, example: [Viewer, Editor, Deleter] }, availablePermissions: { description: 'Permissions available to the user for this project', type: array, items: { type: string, enum: [Viewer, Editor, Deleter] }, example: [Viewer, Editor, Deleter] }, actions: { description: 'Available actions for the user on this project', type: array, items: { type: string, enum: [ProjectUserDelete, ProjectUserUpdate] }, example: [ProjectUserDelete, ProjectUserUpdate] } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 101 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/project-types/permissions-by-user/{id}':
    get:
      tags:
        - 'Tenant/Project Type'
      summary: 'Get  project types with permissions by user'
      description: "Fetches all existing project types with permissions for a specified user, \n            with optional parameters for pagination, project type filtering, and permission filtering."
      operationId: getProjectTypePermissionsByUser
      parameters:
        -
          name: id
          in: path
          description: 'The user ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Maximum number of project types to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: permission
          in: query
          description: 'Filter by a specific permission role'
          required: false
          schema:
            type: string
          example: Viewer
        -
          name: projectTypeId
          in: query
          description: 'Filter by specific project type ID'
          required: false
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  projectTypes: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: b2binpay }, alias: { type: string, example: b2binpay_alias }, availablePermissions: { type: array, items: { type: string, enum: [Viewer, Editor, Creator, Deleter] }, example: [Viewer, Editor, Creator, Deleter] }, actions: { type: array, items: { type: string, enum: [ProjectTypeUserDelete, ProjectTypeUserUpdate] }, example: [ProjectTypeUserDelete, ProjectTypeUserUpdate] } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/project-types:
    get:
      tags:
        - 'Tenant/Project Type'
      summary: 'Get project types with for user'
      description: 'Fetches all existing project types, with optional parameters for pagination'
      operationId: getProjectTypes
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of project types to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
          example: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'Successful response with a list of project types and their permissions'
          content:
            application/json:
              schema:
                properties:
                  projectTypes: { type: array, items: { properties: { id: { type: integer, example: 3 }, name: { type: string, example: b2binpay }, alias: { type: string, example: b2binpay_alias } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 20 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/by-product/{id}':
    get:
      tags:
        - Tenant
      summary: 'Get tenants by product ID'
      description: 'Fetches all tenants for a given product, with optional parameters for pagination and search'
      operationId: getTenantsByProduct
      parameters:
        -
          name: id
          in: path
          description: 'ID of the product to fetch tenants for'
          required: true
          schema:
            type: string
            format: uuid
            example: 8fdd6ca7-490f-4ad7-b65e-fe88cd6692d9
        -
          name: limit
          in: query
          description: 'Maximum number of tenants to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: search
          in: query
          description: 'Optional search term to filter tenants'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Successful response with a list of tenants'
          content:
            application/json:
              schema:
                properties:
                  tenants: { type: array, items: { properties: { id: { type: string, format: uuid, example: ab51808b-43f3-4874-b5ed-2417f62b5b2c }, name: { type: string, example: saas.b2broker }, languages: { type: array, items: { type: integer, example: 1 } }, productId: { type: string, format: uuid, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }, allSourcesCount: { type: integer, example: 465 }, languagesCount: { type: integer, example: 1 } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 1 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/platforms/export:
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get all platforms with update permission'
      description: 'Returns all available platforms where the user has update permission.'
      operationId: platformsExport
      parameters:
        -
          name: platformId
          in: query
          description: 'Platform ID to determine the project'
          required: true
          schema:
            type: integer
            example: 23
        -
          name: languageId
          in: query
          description: 'Target language ID'
          required: true
          schema:
            type: integer
            example: 1
        -
          name: storyId
          in: query
          description: 'ID of the story'
          required: true
          schema:
            type: integer
            example: 1
        -
          name: categoryIds
          in: query
          description: 'Category IDs as JSON array, like [2,4,6]'
          required: false
          schema:
            type: string
            example: '[2,4,6]'
        -
          name: search
          in: query
          description: 'Filter by project name (optional)'
          required: false
          schema:
            type: string
            example: 305markets
      responses:
        '200':
          description: 'List of available platforms'
          content:
            application/json:
              schema:
                type: array
                items:
                  properties: { id: { type: integer, example: 76 }, name: { type: string, example: 'Web Platform' } }
                  type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/platforms/import:
    get:
      tags:
        - Tenant/Platforms
      summary: 'Get platforms by language and base platform'
      description: 'Returns a list of platforms with total key counts for shared categories across same project_type and tenant.'
      operationId: getPlatformsByLanguageAndPlatform
      parameters:
        -
          name: platformId
          in: query
          description: 'ID of the platform'
          required: true
          schema:
            type: integer
          example: 76
        -
          name: languageId
          in: query
          description: 'ID of the language'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: storyId
          in: query
          description: 'ID of the story'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Number of items per page'
          required: false
          schema:
            type: integer
            default: 50
          example: 50
        -
          name: offset
          in: query
          description: 'Offset from the start of the list'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'List of platforms with translated and total key counts'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 57 }, name: { type: string, example: b2broker }, allKeysCount: { type: integer, example: 1805 }, translatedKeysCount: { type: integer, example: 1201 } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 75 } }, type: object }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{id}/by-ai-translator/{aiTranslatorId}':
    get:
      tags:
        - Tenant/Product
      summary: 'Get all available projects by AI translator'
      description: 'Fetches all available projects assigned to an AI translator.'
      operationId: getOneProductByAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the project type'
          required: true
          schema:
            type: string
          example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d
        -
          name: aiTranslatorId
          in: path
          description: 'The AI Translator ID'
          required: true
          schema:
            type: string
          example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d
      responses:
        '200':
          description: 'Successful response with a list of projects and their details'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d }
                  name: { type: string, example: b2core }
                  aiTranslatorId: { type: string, example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d }
                  defaultLanguage: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object }
                  languages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object } }
                  status: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: Active } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{id}/by-ai-translator/{aiTranslatorId}':
    get:
      tags:
        - Tenant
      summary: 'Get one tenant'
      description: 'Fetches one tenant assigned to an AI translator.'
      operationId: getOneTenantByAiTranslator
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the project'
          required: true
          schema:
            type: string
          example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d
        -
          name: aiTranslatorId
          in: path
          description: 'The AI Translator ID'
          required: true
          schema:
            type: string
          example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d
      responses:
        '200':
          description: 'Successful response with a tenant'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d }
                  name: { type: string, example: b2core }
                  productName: { type: string, example: b2core }
                  aiTranslatorId: { type: string, example: 2ebbda91-f875-4a5a-ac37-aa582ca0980d }
                  defaultLanguage: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object }
                  languages: { type: array, items: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: English } }, type: object } }
                  status: { properties: { id: { type: integer, example: 1 }, name: { type: string, example: Active } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{id}/delete-preview':
    get:
      tags:
        - Tenant/Product
      summary: 'Preview product deletion impact'
      description: 'Returns counts of all entities that will be deleted when the product is hard-deleted.'
      operationId: getProductDeletePreview
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the product'
          required: true
          schema:
            type: string
            format: uuid
          example: 7c6c2c68-2def-421a-b4d0-107351f13049
      responses:
        '200':
          description: 'Counts of entities to be deleted'
          content:
            application/json:
              schema:
                properties:
                  projectTypes: { type: integer, example: 3 }
                  tenants: { type: integer, example: 2 }
                  projects: { type: integer, example: 15 }
                  categories: { type: integer, example: 12 }
                  sources: { type: integer, example: 4500 }
                  translations: { type: integer, example: 120000 }
                  stories: { type: integer, example: 8 }
                  storyLanguages: { type: integer, example: 16 }
                  sourceStories: { type: integer, example: 9000 }
                  keysVersions: { type: integer, example: 24 }
                  projectUsers: { type: integer, example: 10 }
                  projectTypeUsers: { type: integer, example: 5 }
                  languageProjects: { type: integer, example: 30 }
                  emptyProjectUsers: { type: integer, example: 2 }
                  categoryProjects: { type: integer, example: 20 }
                  aiTranslatorAssignments: { type: integer, example: 4 }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{uid}/robot-tokens/{id}/renew':
    patch:
      tags:
        - 'Product Robot Tokens'
      summary: 'Renew product robot API token'
      description: 'Renews (rotates) a robot API token. Default expiration: +1 month.'
      operationId: renewProductRobotToken
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
        -
          name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              properties:
                expiresInHours:
                  type: integer
                  maximum: 24
                  minimum: 1
                  nullable: true
                expiresAt:
                  type: string
                  format: date-time
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Renewed successfully.'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid }
                  name: { type: string }
                  createdAt: { type: string, format: date-time }
                  expiresAt: { type: string, format: date-time }
                  token: { type: string }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not found'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{uid}/robot-tokens/{id}/reveal':
    get:
      tags:
        - 'Product Robot Tokens'
      summary: 'Reveal product robot API token value'
      description: 'Returns the decrypted value of a robot API token. Available to Owner/SuperAdmin/Administrator/TeamLead only.'
      operationId: revealProductRobotToken
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
        -
          name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Decrypted token value.'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid }
                  token: { type: string }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not found'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/platforms/categories:
    post:
      tags:
        - Tenant/Platforms
      summary: 'Update platform categories'
      description: 'Updates the categories attached to a platform by platform ID.'
      operationId: updatePlatformCategories
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - platformId
                - categoryIds
              properties:
                platformId:
                  type: integer
                  example: 76
                categoryIds:
                  type: array
                  items: { type: integer }
                  example: [1, 2, 3]
              type: object
      responses:
        '200':
          description: 'Categories updated successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: Ok }
                type: object
        '400':
          description: 'Bad Request'
        '401':
          description: Unauthorized
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/products/{uid}':
    put:
      tags:
        - Tenant/Product
      summary: 'Update product details and platform contexts'
      description: 'Platforms can only be added or have their context updated; removing platforms is not permitted.'
      operationId: updateProduct
      parameters:
        -
          name: uid
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 7c6c2c68-2def-421a-b4d0-107351f13049
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
                - iconName
                - languages
                - defaultLanguageId
                - platforms
              properties:
                name:
                  type: string
                  example: B2Core
                iconName:
                  type: string
                  example: b2core-icon
                languages:
                  type: array
                  items: { type: integer }
                  example: [1, 2, 3, 4, 5]
                defaultLanguageId:
                  type: integer
                  example: 1
                platforms:
                  type: array
                  items: { required: [type], properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object }
              type: object
      responses:
        '200':
          description: 'Product updated successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: 7c6c2c68-2def-421a-b4d0-107351f13049 }
                  name: { type: string, example: B2Core }
                  iconName: { type: string, example: b2core-icon }
                  languages: { type: array, items: { type: integer }, example: [1, 2, 3, 4, 5] }
                  defaultLanguageId: { type: integer, example: 1 }
                  platforms: { type: array, items: { properties: { type: { type: string, example: web }, context: { type: string, example: 'some context', nullable: true } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Product not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/project-types/{id}':
    patch:
      tags:
        - 'Tenant/Project Type'
      summary: 'Update project type context'
      description: 'Updates the context assigned to a project type.'
      operationId: updateProjectTypeContext
      parameters:
        -
          name: id
          in: path
          description: 'Project type identifier'
          required: true
          schema:
            type: integer
          example: 42
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                context:
                  type: string
                  example: 'Updated context'
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Context updated successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: integer, example: 42 }
                  type: { type: string, example: web }
                  context: { type: string, example: 'Updated context', nullable: true }
                  productId: { type: string, format: uuid, example: 7c6c2c68-2def-421a-b4d0-107351f13049, nullable: true }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Project type not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{id}/language-order':
    patch:
      tags:
        - Tenant
      summary: 'Update tenant language order'
      description: 'Sets or resets custom language display ordering for a tenant. Send an array of language IDs to set custom order, or null/empty array to reset to default.'
      operationId: updateTenantLanguageOrder
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the tenant'
          required: true
          schema:
            type: string
            format: uuid
          example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                languageIds:
                  description: 'Ordered array of language IDs, or null/empty to reset'
                  type: array
                  items: { type: integer, example: 3 }
                  example: [3, 1, 5, 2]
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Language order updated successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe }
                  languagesOrder: { type: array, items: { type: integer, example: 3 }, example: [3, 1, 5, 2], nullable: true }
                type: object
        '400':
          description: 'Validation Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{id}/languages':
    patch:
      tags:
        - Tenant
      summary: 'Update an existing tenant'
      description: "Updates a tenant's name and language list."
      operationId: updateTenantLanguages
      parameters:
        -
          name: id
          in: path
          description: 'UUID of the tenant to update'
          required: true
          schema:
            type: string
            format: uuid
          example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                languages:
                  type: array
                  items: { type: integer, example: 1 }
              type: object
      responses:
        '200':
          description: 'Tenant updated successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 420eacee-7d2e-4ea0-ab47-817e9c2d92fe }
                  name: { type: string, example: test2 }
                  languages: { type: array, items: { type: integer, example: 1 } }
                  productId: { type: string, format: uuid, example: 2456e544-60c1-4876-b5d9-531d2cbd7bfa }
                  changerId: { type: integer, example: 5 }
                  createdAt: { type: string, example: '2025-05-03 08:50:11', nullable: false }
                  updatedAt: { type: string, example: null, nullable: true }
                  deletedAt: { type: string, example: null, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/copy-tenant-translations/apply:
    post:
      tags:
        - Translation
      summary: "Copy a tenant's manual translations into platform default translations"
      operationId: ApplyCopyTenantTranslationsToDefault
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - tenantId
                - languageId
              properties:
                projectTypeId:
                  type: integer
                  example: 1
                tenantId:
                  type: string
                  format: uuid
                  example: 11111111-1111-1111-1111-111111111111
                languageId:
                  type: integer
                  example: 2
                overwrite:
                  type: boolean
                  example: false
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Apply counters'
          content:
            application/json:
              schema:
                properties:
                  created: { type: integer, example: 100 }
                  overwritten: { type: integer, example: 20 }
                  skipped: { type: integer, example: 0 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Project type not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  '/api/v2/keys/{id}':
    delete:
      tags:
        - Translation
      summary: 'Archive or permanently delete a translation key'
      description: 'If the key is not archived: soft-deletes it (sets deleted_at = NOW()). If the key is already archived: permanently removes it along with translations, source_story records and writes history. Requires SourceDelete permission for archiving and SourceDeleteDeleted for permanent removal.'
      operationId: archiveOrDeleteKey
      parameters:
        -
          name: id
          in: path
          description: 'Source (key) identifier'
          required: true
          schema:
            type: integer
          example: 42
      responses:
        '200':
          description: 'Key archived successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Key not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/stories/{id}/complete':
    post:
      tags:
        - Story
      summary: 'Complete processing story'
      description: 'Completes the processing story by marking it as done and clearing story languages.'
      operationId: completeStoryManually
      parameters:
        -
          name: id
          in: path
          description: 'Story ID'
          required: true
          schema:
            type: integer
          example: 10
      responses:
        '200':
          description: 'Story marked as done successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  errors: { type: object, additionalProperties: { type: array, items: { type: string, example: 'Story is not in processing state' } } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/json-load-by-language:
    post:
      tags:
        - Translation
      summary: 'Bulk upload translations for a specific language and story'
      operationId: DefaultTranslationsJsonLoadByLanguage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - storyId
                - languageId
                - items
              properties:
                projectTypeId:
                  type: integer
                  example: 1
                storyId:
                  type: integer
                  example: 1
                languageId:
                  type: integer
                  example: 5
                overwrite:
                  type: boolean
                  example: true
                  nullable: true
                items:
                  type: array
                  items: { required: [name, translation], properties: { name: { type: string, example: ScopeBoards.FeatureWidgets.Boards.IbRoom.Widgets.AcquisitionReport.Date }, defaultTranslation: { type: string, example: Date, nullable: true }, translation: { type: string, example: Дата } }, type: object }
              type: object
      responses:
        '200':
          description: 'Successful operation summary'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                  inserted: { type: integer, example: 2 }
                  updated: { type: integer, example: 1 }
                  skipped: { type: integer, example: 0 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/json-download:
    post:
      tags:
        - Translation
      summary: 'Download default translations as JSON file.'
      operationId: DownloadDefaultTranslationsJson
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - storyId
                - languageId
              properties:
                projectTypeId:
                  type: integer
                  example: 5
                storyId:
                  type: integer
                  example: 12
                languageId:
                  type: integer
                  example: 3
                format:
                  description: 'Export file format. Defaults to json.'
                  type: string
                  enum: [json, csv, android_xml]
                  example: json
              type: object
      responses:
        '200':
          description: 'JSON file generated successfully.'
          headers:
            Content-Disposition:
              description: 'Attachment header for file download'
              schema:
                type: string
                example: 'attachment; filename=default-translations.json'
          content:
            application/json:
              schema:
                type: string
                format: binary
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/project-csv-download:
    post:
      tags:
        - Translation
      summary: 'Generate and return a CSV file with translation data.'
      description: 'Download translation data as a CSV file.'
      operationId: DownloadTranslationsProjectCsv
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectId
                - languageId
              properties:
                projectId:
                  description: 'ID of the project'
                  type: integer
                  example: 6
                languageId:
                  description: 'ID of the language'
                  type: integer
                  example: 2
              type: object
      responses:
        '200':
          description: 'CSV file generated successfully.'
          headers:
            Content-Disposition:
              description: 'Attachment header for file download'
              schema:
                type: string
                example: 'attachment; filename=translations.csv'
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '400':
          description: 'Validation error (e.g., invalid language/project/story).'
          content:
            application/json:
              schema:
                properties:
                  errors: { description: 'Key-value pairs with validation errors', type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/csv-download:
    post:
      tags:
        - Translation
      summary: 'Generate and return a CSV file with translation data.'
      description: 'Download translation data as a CSV file.'
      operationId: DownloadTranslationCsv
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectId
                - languageId
                - storyId
                - data
              properties:
                projectId:
                  description: 'ID of the project'
                  type: integer
                  example: 6
                languageId:
                  description: 'ID of the language'
                  type: integer
                  example: 2
                storyId:
                  description: 'ID of the story'
                  type: integer
                  example: 3
                data:
                  description: 'Array of translation source IDs'
                  type: array
                  items: { type: integer }
                  example: [15257, 15258]
              type: object
      responses:
        '200':
          description: 'CSV file generated successfully.'
          headers:
            Content-Disposition:
              description: 'Attachment header for file download'
              schema:
                type: string
                example: 'attachment; filename=translations.csv'
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '400':
          description: 'Validation error (e.g., data is not an array).'
          content:
            application/json:
              schema:
                properties:
                  data: { type: string, example: 'Data should be an array' }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys/export-by-category:
    post:
      tags:
        - Key
      summary: '[DEPRECATED] Export (move) a key to another category'
      description: 'Deprecated. Use POST /api/v2/keys/multiple-export instead.'
      operationId: exportKeyByCategory
      responses:
        '410':
          description: 'Gone — endpoint deprecated'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { error: { type: string, example: 'This endpoint is deprecated. Use POST /api/v2/keys/multiple-export instead.' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
      deprecated: true
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/export-keys:
    post:
      tags:
        - Translation
      summary: 'Send keys from one platform to multiple target platforms by language'
      description: 'Sends selected keys from a source platform to multiple target platforms, matching by language within the same project type.'
      operationId: exportKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - fromPlatformId
                - toPlatformIds
                - languageId
                - data
              properties:
                fromPlatformId:
                  type: integer
                  example: 76
                toPlatformIds:
                  type: array
                  items: { type: integer }
                  example: [23, 24]
                languageId:
                  type: integer
                  example: 1
                data:
                  type: array
                  items: { type: integer }
                  example: [2320, 2321, 2322]
                storyId:
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: 'Keys sent successfully'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations:
    get:
      tags:
        - Translation
      summary: 'Fetch default translations for a platform with optional filters'
      operationId: GetAllDefaultTranslationsByPlatform
      parameters:
        -
          name: projectTypeId
          in: query
          description: 'Identifier of the platform (project type)'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: storyId
          in: query
          description: 'Story identifier'
          required: true
          schema:
            type: integer
          example: 9
        -
          name: languageId
          in: query
          description: 'Language identifier to contextualize results'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: categoryId
          in: query
          description: 'Filter by category identifier'
          required: false
          schema:
            type: integer
          example: 3
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
          example: 0
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
          example: 50
        -
          name: keyId
          in: query
          description: 'Filter by specific key identifier'
          required: false
          schema:
            type: integer
        -
          name: hasNotDestination
          in: query
          description: 'Return only keys without default destination'
          required: false
          schema:
            type: boolean
        -
          name: search
          in: query
          description: 'Search by key or destination'
          required: false
          schema:
            type: string
        -
          name: createdAt
          in: query
          description: 'Created at range (e.g. 2023-01-01,2023-01-31)'
          required: false
          schema:
            type: string
        -
          name: updatedAt
          in: query
          description: 'Updated at range (e.g. 2023-01-01,2023-01-31)'
          required: false
          schema:
            type: string
        -
          name: quickFilter
          in: query
          description: 'Apply predefined quick filter'
          required: false
          schema:
            type: integer
        -
          name: sort
          in: query
          description: 'Sort order (e.g. name, -name)'
          required: false
          schema:
            type: string
          example: '-updatedAt'
      responses:
        '200':
          description: 'Default translations payload'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { type: object } }
                  pagination: { properties: { total: { type: integer, example: 0 } }, type: object }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys:
    get:
      tags:
        - Translation
      summary: 'Get all translation keys with destination by tenant, platform, language and story'
      description: 'Returns key => destination list (merged from translation, source_story, etc)'
      operationId: getAllTranslationKeys
      parameters:
        -
          name: tenantId
          in: query
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
            example: f5aa2ac0-d762-41b2-8d82-212b3d84b47e
        -
          name: platforms
          in: query
          description: 'Platform identifier (e.g. web, ios)'
          required: true
          schema:
            type: array
            items:
              type: string
            example:
              - web
        -
          name: lang
          in: query
          description: 'Language code or BCP tag (e.g. en, fr, ru, en-US). Lookup order: lang_bcp -> lang.'
          required: true
          schema:
            type: string
            example: en-US
        -
          name: storyName
          in: query
          description: 'Optional story name for context'
          required: false
          schema:
            type: string
            example: checkout
      responses:
        '200':
          description: 'Successful key list'
          content:
            application/json:
              schema:
                properties:
                  platforms: { type: array, items: { properties: { name: { type: string, example: web }, keys: { type: object, example: { ScopeBoards.FeatureWidgets.Boards.Shared.Components.OrderBook.Spread: Spread }, additionalProperties: { type: string } } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: 'Missing required query parameter: tenantId' }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Internal Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
  /api/v3/keys:
    get:
      tags:
        - Translation
      summary: 'Get all translation keys with destination by tenant, platform, language and story (v3)'
      description: 'Returns key => destination list (merged from translation, source_story, etc)'
      operationId: getAllTranslationKeysV3
      parameters:
        -
          name: tenantId
          in: query
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
            example: f5aa2ac0-d762-41b2-8d82-212b3d84b47e
        -
          name: platforms
          in: query
          description: 'Platform identifier (e.g. web, ios)'
          required: true
          schema:
            type: array
            items:
              type: string
            example:
              - web
        -
          name: lang
          in: query
          description: 'Language code or BCP tag (e.g. en, fr, ru, en-US). Lookup order: lang_bcp -> lang.'
          required: true
          schema:
            type: string
            example: en-US
        -
          name: storyName
          in: query
          description: 'Optional story name for context'
          required: false
          schema:
            type: string
            example: checkout
      responses:
        '200':
          description: 'Successful key list'
          content:
            application/json:
              schema:
                properties:
                  platforms: { type: array, items: { properties: { name: { type: string, example: web }, keys: { type: object, example: { ScopeBoards.FeatureWidgets.Boards.Shared.Components.OrderBook.Spread: Spread }, additionalProperties: { type: string } } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: 'Missing required query parameter: tenantId' }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '500':
          description: 'Internal Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
  /api/v3/empty:
    get:
      tags:
        - Translation
      summary: 'Returns a simple OK message without touching DB'
      operationId: getEmptyV3
      responses:
        '200':
          description: 'Always OK'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
  /api/v2/keys/merge:
    get:
      tags:
        - Key
      summary: 'Get merge keys data'
      description: 'Returns a paginated list of source keys for merging two stories, comparing translations for a specific language. Status: 1 = only in from-story, 2 = in both (different values), 3 = only in to-story.'
      operationId: getMergeKeysData
      parameters:
        -
          name: sourceStoryId
          in: query
          description: 'Source story ID (from-story)'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: targetStoryId
          in: query
          description: 'Target story ID (to-story)'
          required: true
          schema:
            type: integer
          example: 2
        -
          name: languageId
          in: query
          description: 'Language ID'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: limit
          in: query
          description: 'Pagination limit'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: status
          in: query
          description: 'Filter by merge status (1 = from-story only, 2 = both stories, 3 = to-story only)'
          required: false
          schema:
            type: integer
            enum:
              - 1
              - 2
              - 3
        -
          name: name
          in: query
          description: 'Filter by source name or destination (case-insensitive)'
          required: false
          schema:
            type: string
        -
          name: destination
          in: query
          description: 'Filter by destination value (case-insensitive)'
          required: false
          schema:
            type: string
        -
          name: fulltextSearch
          in: query
          description: 'Full-text search across name and destinations'
          required: false
          schema:
            type: string
        -
          name: categoryId
          in: query
          description: 'Filter by category ID'
          required: false
          schema:
            type: integer
        -
          name: sort
          in: query
          description: 'Sort field. Options: name, -name, status, -status, createdAt, -createdAt'
          required: false
          schema:
            type: string
          example: name
        -
          name: createdAt
          in: query
          description: 'Filter by creation date range (format: yyyy-mm-dd:yyyy-mm-dd)'
          required: false
          schema:
            type: string
          example: '2024-01-01:2024-12-31'
      responses:
        '200':
          description: 'Merge data list'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 123 }, name: { type: string, example: key_name }, projectTypeId: { type: integer, example: 3 }, categoryId: { type: integer, example: 5 }, fromStoryDestination: { type: string, example: 'Value A' }, fromStoryDestinationType: { type: integer, example: 0 }, toStoryDestination: { type: string, example: 'Value B' }, toStoryDestinationType: { type: integer, example: 0 }, status: { type: integer, example: 2 }, createdAt: { type: string, example: '2024-01-15 10:30:00' }, deletedAt: { type: string, example: null, nullable: true } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 100 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Key
      summary: 'Merge keys'
      description: 'Merge source keys from one story to another for a specific language. Upserts translations and optionally deletes the source story.'
      operationId: mergeKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - fromStoryId
                - toStoryId
                - languageId
                - data
              properties:
                fromStoryId:
                  description: 'Source story ID'
                  type: integer
                  example: 1
                toStoryId:
                  description: 'Target story ID'
                  type: integer
                  example: 2
                languageId:
                  description: 'Language ID'
                  type: integer
                  example: 1
                deleteSourceStory:
                  description: 'Delete source story after merge'
                  type: boolean
                  example: false
                autoTranslate:
                  description: 'Trigger auto-translation after merge'
                  type: boolean
                  example: false
                data:
                  description: 'Array of source keys to merge'
                  type: array
                  items: { required: [sourceId], properties: { sourceId: { description: 'Source key ID', type: integer, example: 123 }, destinationType: { description: 'Destination type', type: integer, example: 0 }, destination: { description: 'Translation value', type: string, example: Hello, nullable: true } }, type: object }
              type: object
      responses:
        '200':
          description: 'Merge result'
          content:
            application/json:
              schema:
                properties:
                  changedKeys: { type: integer, example: 5 }
                  publishedChunks: { type: integer, example: 1 }
                type: object
        '400':
          description: 'Bad Request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v3/projects:
    get:
      tags:
        - Translation
      summary: 'Returns a lightweight project list for testing throughput'
      operationId: getProjectsFakeV3
      responses:
        '200':
          description: 'Project list (name + id)'
          content:
            application/json:
              schema:
                properties:
                  projects: { type: array, items: { properties: { id: { type: integer, example: 11 }, name: { type: string, example: 'Demo project' } }, type: object } }
                type: object
  /api/v2/translations/by-platform:
    get:
      tags:
        - Translation
      summary: 'Get translations by platform and filters'
      operationId: GetTranslationsByPlatformV2
      parameters:
        -
          name: projectId
          in: query
          required: true
          schema:
            type: integer
            example: 76
        -
          name: storyId
          in: query
          required: true
          schema:
            type: integer
            example: 1
        -
          name: languageId
          in: query
          required: true
          schema:
            type: integer
            example: 1
        -
          name: categoryId
          in: query
          required: false
          schema:
            type: integer
            example: 0
        -
          name: offset
          in: query
          required: false
          schema:
            type: integer
            example: 0
        -
          name: limit
          in: query
          required: false
          schema:
            type: integer
            example: 50
        -
          name: search
          in: query
          required: false
          schema:
            type: string
        -
          name: name
          in: query
          required: false
          schema:
            type: string
        -
          name: defaultTranslation
          in: query
          required: false
          schema:
            type: string
        -
          name: translation
          in: query
          required: false
          schema:
            type: string
        -
          name: createdAt
          in: query
          required: false
          schema:
            type: string
        -
          name: updatedAt
          in: query
          required: false
          schema:
            type: string
        -
          name: quickFilter
          in: query
          required: false
          schema:
            type: integer
            example: 0
        -
          name: sort
          in: query
          description: 'Sort by field. Available options: "name", "-name", "createdAt", "-createdAt"'
          required: false
          schema:
            type: string
          example: '-name'
      responses:
        '200':
          description: 'Translation data'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { sourceId: { type: integer, example: 1705 }, name: { type: string, example: 'B2Core.Providers.ControlErrors.ErrorTexts.IsRequiredAge [minAge]' }, categoryId: { type: integer, example: 1 }, projectTypeId: { type: integer, example: 1 }, storyId: { type: integer, example: 1 }, is_current_story_translation: { type: boolean, example: true }, destinationType: { type: integer, example: 0 }, translationEng: { type: string, example: Age }, translationLang: { type: string, example: Age }, translation: { type: string, example: 'min age' }, updated_at: { type: string, format: date-time, example: '2025-07-16T17:02:04+04:00' }, created_at: { type: string, format: date-time, example: '2023-05-23T22:20:03+04:00' }, actions: { type: array, items: { type: string }, example: [TranslationUpdate] } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 120 } }, type: object }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/import:
    post:
      tags:
        - Translation
      summary: 'Upload translations for a language/story from a JSON, CSV or Android strings.xml file'
      operationId: ImportDefaultTranslations
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - projectTypeId
                - storyId
                - languageId
                - file
              properties:
                projectTypeId:
                  type: integer
                  example: 1
                storyId:
                  type: integer
                  example: 1
                languageId:
                  type: integer
                  example: 5
                format:
                  description: 'Defaults to the uploaded file extension, then json.'
                  type: string
                  enum: [json, csv, android_xml]
                overwrite:
                  type: boolean
                  example: true
                  nullable: true
                file:
                  type: string
                  format: binary
              type: object
      responses:
        '200':
          description: 'Successful operation summary'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                  inserted: { type: integer, example: 2 }
                  updated: { type: integer, example: 1 }
                  skipped: { type: integer, example: 0 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/import-keys:
    post:
      tags:
        - Translation
      summary: 'Load keys from another platform by language within same project'
      description: 'Loads source keys from one platform to another within the same project, matching by category and language.'
      operationId: importKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - fromPlatformId
                - toPlatformId
                - languageId
              properties:
                fromPlatformId:
                  type: integer
                  example: 24
                toPlatformId:
                  type: integer
                  example: 76
                languageId:
                  type: integer
                  example: 1
                storyId:
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: 'Loaded keys grouped by category'
          content:
            application/json:
              schema:
                properties:
                  categories: { type: array, items: { properties: { category: { type: string, example: auth }, keys: { type: array, items: { properties: { key: { type: string, example: B2Core.Providers.ControlErrors.ErrorTexts.IsRequiredAge }, value: { type: string, example: 'User must be over {minage} y.o.' } }, type: object } } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/load-json:
    post:
      tags:
        - Translation
      summary: 'Bulk import keys (with optional overwrite)'
      operationId: sourceLoadJson
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeAlias:
                  type: string
                storyName:
                  type: string
                data:
                  type: array
                  items: { type: object }
                overwrite:
                  type: boolean
                  nullable: true
                archiveMissing:
                  type: boolean
                  nullable: true
              type: object
      responses:
        '200':
          description: OK
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/load-json-robot:
    post:
      tags:
        - Translation
        - Robot
      summary: 'Bulk import keys (robot) with optional overwrite'
      operationId: sourceLoadJsonRobot
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeId:
                  type: integer
                storyName:
                  type: string
                data:
                  type: object
                  additionalProperties: { type: string }
              type: object
      responses:
        '200':
          description: OK
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/copy-tenant-translations/tenants:
    get:
      tags:
        - Translation
      summary: 'List tenants that own a project on the given platform (copy-tenant-translations source picker)'
      operationId: ListCopyTenantTranslationsTenants
      parameters:
        -
          name: projectTypeId
          in: query
          description: 'Identifier of the platform (project type)'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: search
          in: query
          description: 'Case-insensitive substring filter on tenant name'
          required: false
          schema:
            type: string
          example: demo
        -
          name: limit
          in: query
          description: 'Pagination limit (1-1500)'
          required: false
          schema:
            type: integer
          example: 50
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
          example: 0
      responses:
        '200':
          description: 'Tenants owning a non-deleted project on the platform'
          content:
            application/json:
              schema:
                properties:
                  items: { type: array, items: { properties: { id: { type: string, format: uuid, example: 11111111-1111-1111-1111-111111111111 }, name: { type: string, example: DEMO } }, type: object } }
                  meta: { properties: { limit: { type: integer, example: 50 }, offset: { type: integer, example: 0 }, total: { type: integer, example: 12 } }, type: object }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Project type not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/translation-histories:
    get:
      tags:
        - Translation
      summary: 'List translation version history for a given key and scope'
      operationId: ListTranslationHistoriesV2
      parameters:
        -
          name: type
          in: query
          description: '0 = project_type (default), 1 = project (custom)'
          required: true
          schema:
            type: integer
            example: 0
        -
          name: itemId
          in: query
          required: true
          schema:
            type: integer
            example: 1
        -
          name: sourceId
          in: query
          required: true
          schema:
            type: integer
            example: 123
        -
          name: storyId
          in: query
          required: true
          schema:
            type: integer
            example: 9
        -
          name: languageId
          in: query
          required: true
          schema:
            type: integer
            example: 1
        -
          name: offset
          in: query
          required: false
          schema:
            type: integer
            example: 0
        -
          name: limit
          in: query
          required: false
          schema:
            type: integer
            example: 10
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { id: { type: integer, example: 42 }, type: { type: integer, example: 0 }, itemId: { type: integer, example: 1 }, sourceId: { type: integer, example: 123 }, storyId: { type: integer, example: 9 }, languageId: { type: integer, example: 1 }, destination: { type: string, nullable: true }, changeSource: { type: string, example: manual_update }, changeSourceCategory: { type: string, example: manual }, changerId: { type: integer, example: 5 }, changerEmail: { type: string, example: dkarapetyan@b2broker.com, nullable: true }, createdAt: { type: string, example: '2026-04-23 10:00:00' } }, type: object } }
                  pagination: { properties: { total: { type: integer, example: 100 } }, type: object }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/load:
    post:
      tags:
        - Translation
      summary: 'Load default translations'
      description: 'Creates new source keys with initial translations for a given project type and story.'
      operationId: loadDefaultTranslations
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeAlias
                - storyName
                - data
              properties:
                projectTypeAlias:
                  description: 'Project type alias'
                  type: string
                  example: my-project
                storyName:
                  description: 'Story name'
                  type: string
                  example: develop
                data:
                  description: 'Array of source keys to create'
                  type: array
                  items: { properties: { name: { description: 'Key name', type: string, example: common.button.save }, destinationType: { description: '0 = Text, 1 = HTML', type: integer, example: 0 }, destination: { description: 'Default translation value', type: string, example: Save } }, type: object }
              type: object
      responses:
        '200':
          description: 'Keys created successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Bad Request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/contexts:
    post:
      tags:
        - Translation
      summary: 'Queue context parsing for keys without context'
      description: 'Queues asynchronous context parsing for all keys within the given project type that have empty context values.'
      operationId: queueLoadKeyContexts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeId:
                  description: 'Identifier of the project type to process'
                  type: integer
                  example: 12
              type: object
      responses:
        '202':
          description: 'Context loading queued'
          content:
            application/json:
              schema:
                properties:
                  status: { type: string, example: queued }
                type: object
        '400':
          description: 'Invalid input'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/load-keys:
    post:
      tags:
        - Translation
      summary: 'Load new translation keys'
      description: 'Adds one or more new translation keys with translations for the given project type, story and category.'
      operationId: loadTranslationKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeAlias:
                  type: string
                  example: b2core_alias
                storyName:
                  type: string
                  example: production
                categoryId:
                  type: integer
                  example: 9
                languageId:
                  type: integer
                  example: 1
                data:
                  type: array
                  items: { properties: { name: { type: string, example: welcome_title }, destination: { type: string, example: 'Welcome to the homepage' }, destinationType: { type: integer, example: 0 } }, type: object }
              type: object
      responses:
        '200':
          description: 'Keys successfully loaded'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
        '400':
          description: 'Invalid input'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
        '500':
          description: 'Internal server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/migrate:
    post:
      tags:
        - Translation
      summary: 'Migrate default translation keys within a project type'
      operationId: DefaultTranslationsMigrate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - mapping
              properties:
                projectTypeId:
                  type: integer
                  example: 13
                overwrite:
                  type: boolean
                  example: false
                  nullable: true
                removeOldKeysOnSuccess:
                  type: boolean
                  example: false
                  nullable: true
                mapping:
                  type: object
                  example: { Old.Key.A: New.Key.A, Old.Key.B: New.Key.B }
                  additionalProperties: { type: string }
              type: object
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                properties:
                  defaultTranslation: { properties: { created: { type: integer, example: 0 }, updated: { type: integer, example: 0 }, skipped: { type: integer, example: 0 } }, type: object }
                  translation: { properties: { created: { type: integer, example: 0 }, updated: { type: integer, example: 0 }, skipped: { type: integer, example: 0 } }, type: object }
                type: object
        '400':
          description: 'Bad request'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys/multiple-export:
    post:
      tags:
        - Key
      summary: 'Move multiple keys to another category'
      description: 'Moves multiple source keys to a different category within the same project type. Creates history records for each moved key.'
      operationId: multipleExportKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - categoryId
                - data
                - storyId
              properties:
                categoryId:
                  description: 'Target category ID'
                  type: integer
                  example: 5
                data:
                  description: 'Array of source key IDs to move'
                  type: array
                  items: { type: integer, example: 42 }
                storyId:
                  description: 'Story ID context'
                  type: integer
                  example: 10
              type: object
      responses:
        '200':
          description: 'Keys moved successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Bad Request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/copy-tenant-translations/preview:
    get:
      tags:
        - Translation
      summary: "Preview copying a tenant's manual translations into platform default translations"
      operationId: PreviewCopyTenantTranslationsToDefault
      parameters:
        -
          name: projectTypeId
          in: query
          description: 'Identifier of the platform (project type)'
          required: true
          schema:
            type: integer
          example: 1
        -
          name: tenantId
          in: query
          description: 'Tenant UUID whose manual translations are the copy source'
          required: true
          schema:
            type: string
            format: uuid
          example: 11111111-1111-1111-1111-111111111111
        -
          name: languageId
          in: query
          description: 'Target language to copy (must not be the platform base language)'
          required: true
          schema:
            type: integer
          example: 2
        -
          name: overwrite
          in: query
          description: 'Whether existing defaults would be overwritten on apply'
          required: false
          schema:
            type: boolean
          example: false
        -
          name: limit
          in: query
          description: 'Pagination limit (1-1500)'
          required: false
          schema:
            type: integer
          example: 50
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
          example: 0
      responses:
        '200':
          description: 'Diff preview'
          content:
            application/json:
              schema:
                properties:
                  items: { type: array, items: { properties: { key: { type: string, example: login_button }, language: { type: integer, example: 2 }, oldValue: { type: string, example: null, nullable: true }, newValue: { type: string, example: 'Se connecter', nullable: true }, action: { type: string, enum: [create, overwrite], example: create } }, type: object } }
                  meta: { properties: { limit: { type: integer, example: 50 }, offset: { type: integer, example: 0 }, total: { type: integer, example: 120 } }, type: object }
                  counters: { properties: { create: { type: integer, example: 100 }, overwrite: { type: integer, example: 20 }, total: { type: integer, example: 120 } }, type: object }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Project type not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/proofread-default:
    post:
      tags:
        - Translation
      summary: 'Proofread default language translations and fix plural keys via AI'
      description: 'Publishes a proofread task to the queue. The consumer reads all source_story entries for the given projectType + story in the default language, sends them to AI for proofreading and plural key fixing, then updates changed records.'
      operationId: proofreadDefaultLanguage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - storyId
              properties:
                projectTypeId:
                  type: integer
                  example: 8
                storyId:
                  type: integer
                  example: 1
                includeManual:
                  description: "When false (default), manually-translated keys are skipped so AI proofreading never overwrites a human's translation. Set true to also proofread manual translations."
                  type: boolean
                  default: false
                  example: false
              type: object
      responses:
        '202':
          description: 'Proofread task accepted'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: 'Proofread task accepted' }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/proofread-languages:
    post:
      tags:
        - Translation
      summary: 'Proofread story languages and auto-fix translations via AI'
      description: 'Publishes a proofread-languages task to the queue. The consumer reads default language text and translates/re-translates to other languages of the project type, then updates changed records. If `languages` is provided, only those languages will be proofread; otherwise all non-default languages of the project type are processed.'
      operationId: proofreadLanguages
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - storyId
              properties:
                projectTypeId:
                  type: integer
                  example: 8
                storyId:
                  type: integer
                  example: 1
                languages:
                  description: 'Optional list of language IDs to restrict proofreading to. Omit or send an empty array to proofread all non-default languages of the project type.'
                  type: array
                  items: { type: integer, example: 12 }
                includeManual:
                  description: "When false (default), keys whose target translation is manual are skipped so AI proofreading never overwrites a human's translation. Set true to also proofread manual translations."
                  type: boolean
                  default: false
                  example: false
              type: object
      responses:
        '202':
          description: 'Proofread languages task accepted'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: 'Proofread languages task accepted' }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/remove-archived:
    post:
      tags:
        - Translation
      summary: 'Remove all soft-deleted (archived) translation keys for given project type'
      description: 'Deletes all sources with deleted_at NOT NULL for the given project_type__id. Requires SourceDelete permission.'
      operationId: removeArchivedTranslationKeys
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                projectTypeId:
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { projectTypeId: { type: string, example: 'Invalid project type ID' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/keys/{sourceId}/rename':
    patch:
      tags:
        - Translation
      summary: 'Rename a source key'
      description: 'Renames a source key within a project type. For non-production stories the key must not exist in any other story (checked against the product default language). Increments keys_version for all languages on success.'
      operationId: renameKey
      parameters:
        -
          name: sourceId
          in: path
          description: 'Source (key) identifier'
          required: true
          schema:
            type: integer
          example: 42
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - name
                - storyId
              properties:
                name:
                  description: 'New key name'
                  type: string
                  example: Auth.Login.NewTitle
                storyId:
                  description: 'Story identifier'
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: 'Key renamed successfully'
          content:
            application/json:
              schema:
                required:
                  - id
                  - name
                properties:
                  id: { type: integer, example: 42 }
                  name: { type: string, example: Auth.Login.NewTitle }
                type: object
        '400':
          description: 'Validation error — name is empty or already taken'
        '401':
          description: Unauthorized
        '403':
          description: 'Forbidden — non-production story and key exists in multiple stories'
        '404':
          description: 'Source or story not found'
      security:
        -
          BearerAuthObject: []
  /api/v2/translation-histories/restore:
    post:
      tags:
        - Translation
      summary: 'Restore a destination to a previously recorded version'
      operationId: RestoreTranslationVersionV2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - type
                - itemId
                - versionId
              properties:
                type:
                  description: '0 = project_type (default), 1 = project (custom)'
                  type: integer
                  example: 0
                itemId:
                  type: integer
                  example: 1
                versionId:
                  type: integer
                  example: 42
              type: object
      responses:
        '200':
          description: 'OK — updated source payload'
        '400':
          description: 'Validation error (wrong scope, invalid type)'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Version / source / project not found'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys/rollback:
    put:
      tags:
        - Translation
      summary: 'Rollback (restore) an archived translation key'
      description: 'Restores an archived (soft-deleted) key by clearing its deleted_at timestamp. Also restores all associated translations and records history. Requires SourceRollback permission.'
      operationId: rollbackKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - sourceId
                - storyId
              properties:
                sourceId:
                  description: 'Source (key) identifier'
                  type: integer
                  example: 28257
                storyId:
                  description: 'Story identifier'
                  type: integer
                  example: 3
              type: object
      responses:
        '200':
          description: 'Key restored successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
        '400':
          description: 'Bad request (key not archived or invalid source story)'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Key not found'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys/rollback-bulk:
    put:
      tags:
        - Translation
      summary: 'Bulk-rollback (restore) archived translation keys'
      description: "Restores multiple archived (soft-deleted) keys in one request. Keys that do not exist or are not archived are silently skipped — the response reports counts. Requires SourceRollback permission on the story's project_type."
      operationId: rollbackKeysBulk
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - sourceIds
                - storyId
              properties:
                sourceIds:
                  description: 'Array of source (key) identifiers to restore'
                  type: array
                  items: { type: integer }
                  example: [28257, 28258, 28259]
                storyId:
                  description: 'Story identifier'
                  type: integer
                  example: 3
              type: object
      responses:
        '200':
          description: 'Bulk rollback completed (partial success possible)'
          content:
            application/json:
              schema:
                properties:
                  restored: { description: 'Number of keys actually restored', type: integer, example: 5 }
                  skipped: { description: 'Number of keys skipped (missing or not archived)', type: integer, example: 2 }
                type: object
        '400':
          description: 'Bad request (invalid payload)'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/ai-update:
    post:
      tags:
        - Translation
      summary: 'Update default translation by ai'
      description: 'ai update default translation'
      operationId: defaultTranslationAiUpdate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - sourceId
                - languageId
              properties:
                projectTypeId:
                  type: integer
                  example: 1
                sourceId:
                  type: integer
                  example: 1
                languageId:
                  type: integer
                  example: 1
                storyId:
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: 'Translated via ai'
          content:
            application/json:
              schema:
                properties:
                  translation: { type: string, example: Bonjour }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { projectTypeId: { type: string, example: 'Invalid project type' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/update:
    patch:
      tags:
        - Translation
      summary: 'Update source destination (name) for a specific story/language (V1 parity)'
      operationId: DestinationUpdate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - sourceId
                - storyId
                - languageId
              properties:
                sourceId:
                  type: integer
                  example: 1705
                storyId:
                  type: integer
                  example: 9
                languageId:
                  type: integer
                  example: 1
                projectTypeId:
                  type: integer
                  example: 1
                hasNotTranslation:
                  type: boolean
                  example: false
                translation:
                  type: string
                  example: 'Currency code'
                destinationType:
                  type: integer
                  example: 0
              type: object
      responses:
        '200':
          description: 'OK — returns updated source payload'
          content:
            application/json:
              schema:
                properties:
                  sourceId: { type: integer, example: 1705 }
                  sourceName: { type: string, example: B2Core.Shared.UiKit.CodeConfirm.CodeConfirmDialog.Email.Hint }
                  categoryId: { type: integer, example: 1 }
                  projectTypeId: { type: integer, example: 1 }
                  storyId: { type: integer, example: 274 }
                  languageId: { type: integer, example: 1 }
                  destination: { type: string, example: CRCER, nullable: true }
                  destinationType: { type: integer, example: 0 }
                  aiTranslatorId: { type: integer, example: null, nullable: true }
                  changerId: { type: integer, example: 5 }
                  createdAt: { type: string, example: '2025-10-06 15:41:43', nullable: true }
                  updatedAt: { type: string, example: '2025-10-06 15:41:43', nullable: true }
                  deletedAt: { type: string, example: null, nullable: true }
                  defaultLanguageId: { type: integer, example: 1 }
                  defaultDestination: { type: string, example: 'Scan to Download App IOS', nullable: true }
                type: object
        '400':
          description: 'Validation error (source/story/language not found, archived key, duplicate name, etc.)'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: 'Project not found'
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/keys/context-update:
    post:
      tags:
        - Translation
      summary: 'Update contexts for keys within a project type'
      description: 'Assigns custom context strings to the provided keys that belong to the given project type.'
      operationId: updateKeyContexts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - keys
              properties:
                projectTypeId:
                  type: integer
                  example: 42
                keys:
                  type: object
                  example: { Auth.Login.Title: 'Visible on the login page header', Dashboard.Info.Balance: 'Displayed near total balance card' }
                  additionalProperties: { type: string }
                overwrite:
                  description: 'When true, replaces existing context values. Defaults to false.'
                  type: boolean
                  example: true
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Contexts updated'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: ok }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/platform-translation:
    post:
      tags:
        - Translation
      summary: 'Update a translation for specific platform/category/language/story/source'
      operationId: UpdatePlatformTranslation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - project_id
                - category_id
                - language_id
                - story_id
                - source_id
                - destination_type
              properties:
                projectId:
                  type: integer
                  example: 76
                languageId:
                  type: integer
                  example: 1
                sourceId:
                  type: integer
                  example: 1705
                storyId:
                  type: integer
                  example: 9
                hasNotTranslation:
                  type: boolean
                  example: false
                destinationType:
                  type: integer
                  example: 0
                translation:
                  type: string
                  example: 'Updated value or null'
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Updated translation response'
          content:
            application/json:
              schema:
                properties:
                  sourceId: { type: integer, example: 1705 }
                  name: { type: string, example: key.name }
                  categoryId: { type: integer, example: 305, nullable: true }
                  projectTypeId: { type: integer, example: 50, nullable: true }
                  storyId: { type: integer, example: 274 }
                  destinationType: { type: integer, example: 0, nullable: true }
                  isCurrentStoryTranslation: { type: boolean, example: false }
                  translation: { type: string, example: 'Localized text', nullable: true }
                  createdAt: { type: string, example: '2025-10-06 15:41:43', nullable: true }
                  updatedAt: { type: string, example: '2025-10-06 15:41:43', nullable: true }
                  actions: { type: array, items: { type: string }, example: [TranslationUpdate, TranslationAiUpdate] }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/default-translations/project-type-translation-by-ai:
    post:
      tags:
        - Translation
      summary: 'Update a translation for specific projectType/language/story/source'
      operationId: UpdateProjectTypeTranslationByAi
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypeId
                - languageId
              properties:
                projectTypeId:
                  type: integer
                  example: 8
                languageIds:
                  type: array
                  items: { type: integer, example: 1 }
                  minItems: 1
                  example: [2, 5]
              type: object
      responses:
        '200':
          description: 'Updated translation response'
          content:
            application/json:
              schema:
                properties:
                  data: { type: object }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
        '500':
          description: 'Server error'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/ai-update:
    post:
      tags:
        - Translation
      summary: 'Update translation by ai'
      description: 'ai update translation'
      operationId: TranslationAiUpdate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectId
                - sourceId
                - languageId
              properties:
                projectId:
                  type: integer
                  example: 1
                sourceId:
                  type: integer
                  example: 1
                languageId:
                  type: integer
                  example: 1
              type: object
      responses:
        '200':
          description: 'Translated via ai'
          content:
            application/json:
              schema:
                properties:
                  translation: { type: string, example: Bonjour }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { projectId: { type: string, example: 'Invalid project' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/translations/csv-update:
    post:
      tags:
        - Translation
      summary: 'Update translations via CSV file upload.'
      description: 'Uploads a CSV file to update translations.'
      operationId: UpdateTranslationCsv
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - file
                - projectId
                - languageId
              properties:
                file:
                  description: 'CSV file containing translations.'
                  type: string
                  format: binary
                projectId:
                  description: 'ID of the project.'
                  type: integer
                  example: 6
                languageId:
                  description: 'ID of the language.'
                  type: integer
                  example: 2
                storyId:
                  description: 'ID of the story.'
                  type: integer
                  example: 3
              type: object
      responses:
        '200':
          description: 'CSV processed successfully.'
          content:
            application/json:
              schema:
                properties:
                  message: { required: [updated, ignored], properties: { updated: { description: 'Count of updated keys', type: integer, example: 3 }, ignored: { description: 'Count of ignored keys', type: integer, example: 3 } }, type: object }
                type: object
        '400':
          description: 'Invalid request parameters.'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { file: { type: string, example: 'File is required.' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/users/me/password:
    patch:
      tags:
        - User/Account
      summary: 'Change my password'
      description: 'Self-service password change for the authenticated user.'
      operationId: changeMyPassword
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - currentPassword
                - newPassword
                - newPasswordConfirmation
              properties:
                currentPassword:
                  type: string
                  format: password
                newPassword:
                  type: string
                  format: password
                newPasswordConfirmation:
                  type: string
                  format: password
              type: object
      responses:
        '204':
          description: 'Password changed'
        '400':
          description: 'Validation error (policy / mismatch / same as current / wrong current password)'
        '401':
          description: Unauthenticated
        '500':
          description: 'Server Error'
      security:
        -
          BearerAuthObject: []
  /api/v2/personal-api-tokens:
    get:
      tags:
        - 'Personal API Tokens'
      summary: 'Get personal API tokens'
      description: 'Returns a list of personal API tokens for the authenticated user.'
      operationId: getPersonalApiTokens
      responses:
        '200':
          description: 'Successful response with list of personal API tokens'
          content:
            application/json:
              schema:
                type: array
                items:
                  properties: { id: { type: string, format: uuid, example: 01964e3a-7e1c-7000-8000-000000000001 }, name: { type: string, example: 'My laptop' }, createdAt: { type: string, format: date-time, example: '2026-03-23T10:00:00+00:00' }, expiresAt: { type: string, format: date-time, example: '2026-03-24T10:00:00+00:00' }, isActive: { type: boolean, example: true }, actions: { type: array, items: { type: string, example: PersonalApiTokenRenew } } }
                  type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - 'Personal API Tokens'
      summary: 'Create personal API token'
      description: 'Creates a new personal API token for the authenticated user. Maximum 2 active tokens allowed.'
      operationId: createPersonalApiToken
      requestBody:
        description: 'Provide either `expiresAt` (custom expiration up to 1 year) or `expiresInHours` (legacy, 1–24). Exactly one must be supplied.'
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  type: string
                  example: 'My laptop'
                expiresInHours:
                  description: 'Legacy hours-based expiration (1–24). Mutually exclusive with `expiresAt`.'
                  type: integer
                  maximum: 24
                  minimum: 1
                  example: 12
                  nullable: true
                expiresAt:
                  description: 'Custom expiration as ISO-8601 date-time or YYYY-MM-DD (treated as end of day UTC). Must be in the future and within 1 year. Mutually exclusive with `expiresInHours`.'
                  type: string
                  format: date-time
                  example: '2026-07-01T00:00:00Z'
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Successfully created personal API token. The token value is returned only once.'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 01964e3a-7e1c-7000-8000-000000000001 }
                  name: { type: string, example: 'My laptop' }
                  createdAt: { type: string, format: date-time, example: '2026-03-23T10:00:00+00:00' }
                  expiresAt: { type: string, format: date-time, example: '2026-03-23T22:00:00+00:00' }
                  token: { type: string, example: eyJ0eXAi... }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { name: { type: string, example: 'Name is required' }, expiresInHours: { type: string, example: 'Expires in hours must be between 1 and 24' }, expiresAt: { type: string, example: 'expiresAt must be within 1 year' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '409':
          description: 'Conflict — maximum active tokens reached'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { name: { type: string, example: 'Maximum 2 active personal API tokens allowed' } }, type: object }
                type: object
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/users:
    get:
      tags:
        - User
      summary: 'Get users list'
      description: 'Returns users with optional filtering by email, status, roles, and tenants. Pagination and sorting mirror v1 behaviour with updated tenant support.'
      operationId: getUsers
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of users to return.'
          required: false
          schema:
            type: integer
            default: 30
        -
          name: offset
          in: query
          description: 'Offset to start pagination.'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: email
          in: query
          description: 'Filter users by email (partial match).'
          required: false
          schema:
            type: string
        -
          name: fullName
          in: query
          description: 'Filter users by full name (partial match).'
          required: false
          schema:
            type: string
        -
          name: fullTextSearch
          in: query
          description: 'Full-text search across email and full name.'
          required: false
          schema:
            type: string
        -
          name: isActive
          in: query
          description: 'Filter by active status.'
          required: false
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
        -
          name: isConfirmed
          in: query
          description: 'Filter by confirmation status.'
          required: false
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
        -
          name: isTwoFaActive
          in: query
          description: 'Filter by 2FA status.'
          required: false
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
        -
          name: roles
          in: query
          description: 'JSON encoded array of role names to filter by.'
          required: false
          schema:
            type: string
        -
          name: tenants
          in: query
          description: 'JSON encoded array of tenant IDs to filter by.'
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sort expression. Supports core fields and tenantsCount.'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Successful response with a list of users'
          content:
            application/json:
              schema:
                properties:
                  users: { type: array, items: { properties: { id: { type: integer, example: 11 }, email: { type: string, example: user@example.com }, fullName: { type: string, example: 'John Doe' }, isActive: { type: boolean, example: true }, isConfirmed: { type: boolean, example: true }, isTwoFaActive: { type: boolean, example: false }, deletedAt: { type: string, format: date-time, example: null, nullable: true }, tenantsCount: { type: integer, example: 2 }, roles: { type: array, items: { type: string }, example: [Administrator] }, actions: { type: array, items: { type: string }, example: [UserUpdate, UserDelete] } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 120 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - User
      summary: 'Create user'
      description: 'Creates a new user and sends a confirmation email.'
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - email
                - fullName
                - roles
              properties:
                email:
                  type: string
                fullName:
                  type: string
                roles:
                  type: array
                  items: { type: string }
              type: object
      responses:
        '201':
          description: Created
        '400':
          description: 'Validation error'
        '403':
          description: Forbidden
        '409':
          description: 'Email already exists, or a subscription plan limit was reached (`planLimit`)'
      security:
        -
          BearerAuthObject: []
  '/api/v2/personal-api-tokens/{id}':
    delete:
      tags:
        - 'Personal API Tokens'
      summary: 'Delete personal API token'
      description: 'Permanently deletes a personal API token.'
      operationId: deletePersonalApiToken
      parameters:
        -
          name: id
          in: path
          description: 'The personal API token UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 01964e3a-7e1c-7000-8000-000000000001
      responses:
        '200':
          description: 'Token deleted successfully'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Token not found'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { id: { type: string, example: 'Personal API token not found' } }, type: object }
                type: object
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{id}':
    get:
      tags:
        - User
      summary: 'Get user by id'
      description: 'Returns single user data (excluding self).'
      operationId: getUserById
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful response'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - User
      summary: 'Delete user'
      description: 'Soft delete on first call; hard delete on a second call for users with UserDeleteDeleted.'
      operationId: deleteUser
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'No Content'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
    patch:
      tags:
        - User
      summary: 'Update user'
      description: 'Partially updates a user. Each field is optional and governed by a separate permission.'
      operationId: updateUser
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email:
                  type: string
                fullName:
                  type: string
                isActive:
                  type: boolean
                isTwoFaActive:
                  type: boolean
                roles:
                  type: array
                  items: { type: string }
              type: object
      responses:
        '200':
          description: Updated
        '400':
          description: 'Validation error'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '409':
          description: 'Email already exists'
      security:
        -
          BearerAuthObject: []
  /api/v2/roles/permissions:
    get:
      tags:
        - User
      summary: 'Get all permissions'
      description: 'Fetch all available permissions grouped by category.'
      operationId: getAllPermissions
      responses:
        '200':
          description: 'Successful response with all permissions'
          content:
            application/json:
              schema:
                properties:
                  permissions: { type: object, example: { CategoryCategory: [CategoryIndex, CategoryCreate] } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/roles:
    get:
      tags:
        - User/Role
      summary: 'Get all roles'
      description: 'Fetches roles assignable by the current caller. Filters by `actorMaxPriority` and workspace scope. Each row carries the descriptor shape { name, displayName, priority, isSystem } plus `usersCount` and `actions`.'
      operationId: getRoles
      parameters:
        -
          name: limit
          in: query
          description: 'Maximum number of roles to return (pagination limit)'
          required: false
          schema:
            type: integer
            default: 10
          example: 10
        -
          name: offset
          in: query
          description: 'Offset to start pagination'
          required: false
          schema:
            type: integer
            default: 0
          example: 0
        -
          name: search
          in: query
          description: 'Filter roles by name'
          required: false
          schema:
            type: string
          example: Administrator
      responses:
        '200':
          description: 'Successful response with a list of roles'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { name: { type: string, example: Administrator }, displayName: { type: string, example: Administrator }, priority: { type: integer, example: 600 }, isSystem: { type: boolean, example: true }, usersCount: { type: integer, example: 28 }, actions: { type: array, items: { type: string, enum: [RoleCreate, RoleUpdate, RoleDelete], example: RoleUpdate } } }, type: object } }
                  pagination: { properties: { maxCount: { type: integer, example: 6 } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/users/available-roles:
    get:
      tags:
        - User
      summary: 'Get available roles'
      description: 'Returns the list of roles the current user can assign when creating or updating another user. Each role carries the descriptor shape { name, displayName, priority, isSystem } and the list is sorted by priority DESC.'
      operationId: getAvailableUserRoles
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                properties:
                  data: { type: array, items: { properties: { name: { type: string, example: Administrator }, displayName: { type: string, example: Administrator }, priority: { type: integer, example: 600 }, isSystem: { type: boolean, example: true } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/me:
    get:
      tags:
        - User
      summary: 'Get user data and permissions'
      description: 'Fetches the user data including email, full name, roles, and permissions.'
      operationId: getMe
      responses:
        '200':
          description: 'Successful response with user data and permissions'
          content:
            application/json:
              schema:
                properties:
                  userData: { properties: { email: { type: string, example: root@b2bweblate.local }, fullName: { type: string, example: null, nullable: true }, roles: { type: array, items: { properties: { name: { type: string, example: Owner }, displayName: { type: string, example: Owner }, priority: { type: integer, example: 800 }, isSystem: { type: boolean, example: true } }, type: object } } }, type: object }
                  permissions: { type: array, items: { type: string, example: CategoryCreate } }
                  workspace: { description: "The caller's own workspace, when they have exactly one. `null` for cross-workspace users (root/Owner/SuperAdmin), who resolve workspaces via the workspace switcher instead.", properties: { id: { type: string, format: uuid }, name: { type: string, example: 'Acme Inc' }, slug: { type: string, example: acme-inc } }, type: object, nullable: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/roles/{name}':
    get:
      tags:
        - User
      summary: 'Get role by name'
      description: 'Fetch detailed information about a role by its name, including permissions grouped by category.'
      operationId: getRoleByName
      responses:
        '200':
          description: 'Successful response with role details'
          content:
            application/json:
              schema:
                properties:
                  name: { type: string, example: Administrator }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/user/remove-two-fa/{id}':
    delete:
      tags:
        - User
      summary: 'Remove User two fa'
      description: 'Remove user two fa'
      operationId: removeUserTwoFa
      parameters:
        -
          name: id
          in: path
          description: 'The user ID'
          required: true
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful response with user data and permissions'
          content:
            application/json:
              schema:
                properties:
                  message: { type: string, example: OK }
                type: object
        '400':
          description: 'Invalid request parameters'
          content:
            application/json:
              schema:
                properties:
                  errors: { properties: { id: { type: array, items: { type: string, example: 'User authenticator does not exist' } } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{userId}/projects/{projectId}':
    delete:
      tags:
        - User
      summary: 'Remove user from project'
      description: 'Full reset of project-user permissions (deletes project_user and empty_project_user rows, restoring inheritance via project_type_user).'
      operationId: removeUserFromProject
      parameters:
        -
          name: userId
          in: path
          required: true
          schema:
            type: integer
        -
          name: projectId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'No Content'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{userId}/project-types/{projectTypeId}':
    delete:
      tags:
        - User
      summary: 'Remove user from project type (cascade)'
      description: 'Cascade delete: removes project_type_user, empty_project_user and project_user rows for (user, projectType).'
      operationId: removeUserFromProjectType
      parameters:
        -
          name: userId
          in: path
          required: true
          schema:
            type: integer
        -
          name: projectTypeId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'No Content'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/personal-api-tokens/{id}/renew':
    patch:
      tags:
        - 'Personal API Tokens'
      summary: 'Renew personal API token'
      description: 'Extends the expiration of a personal API token. Accepts an optional custom `expiresAt` (up to 1 year) or legacy `expiresInHours` (1–24). When neither is provided, defaults to +24 hours.'
      operationId: renewPersonalApiToken
      parameters:
        -
          name: id
          in: path
          description: 'The personal API token UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 01964e3a-7e1c-7000-8000-000000000001
      requestBody:
        description: 'Optional. Provide either `expiresAt` or `expiresInHours` (mutually exclusive). When omitted, the token is extended by 24 hours.'
        required: false
        content:
          application/json:
            schema:
              properties:
                expiresInHours:
                  description: 'Legacy hours-based expiration (1–24). Mutually exclusive with `expiresAt`.'
                  type: integer
                  maximum: 24
                  minimum: 1
                  example: 12
                  nullable: true
                expiresAt:
                  description: 'Custom expiration as ISO-8601 date-time or YYYY-MM-DD (treated as end of day UTC). Must be in the future and within 1 year. Mutually exclusive with `expiresInHours`.'
                  type: string
                  format: date-time
                  example: '2026-07-01T00:00:00Z'
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Token renewed successfully'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, format: uuid, example: 01964e3a-7e1c-7000-8000-000000000001 }
                  name: { type: string, example: 'My laptop' }
                  createdAt: { type: string, format: date-time, example: '2026-03-23T10:00:00+00:00' }
                  expiresAt: { type: string, format: date-time, example: '2026-03-25T10:00:00+00:00' }
                  token: { type: string, example: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { expiresInHours: { type: string, example: 'Expires in hours must be between 1 and 24' }, expiresAt: { type: string, example: 'expiresAt must be within 1 year' } }, type: object }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Token not found'
          content:
            application/json:
              schema:
                properties:
                  message: { properties: { id: { type: string, example: 'Personal API token not found' } }, type: object }
                type: object
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{id}/resend-confirmation-link':
    post:
      tags:
        - User
      summary: 'Resend confirmation link'
      description: 'Regenerates UUID token and emails confirmation link.'
      operationId: resendUserConfirmationLink
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '202':
          description: Accepted
        '400':
          description: 'User already confirmed'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{id}/reset-password':
    post:
      tags:
        - User
      summary: 'Reset user password'
      description: 'Resets user password and sends the new password via email.'
      operationId: resetUserPassword
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '202':
          description: Accepted
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{userId}/project-permissions':
    put:
      tags:
        - User
      summary: 'Update user project permissions'
      description: 'Replace project-level permissions for a user across the listed projects.'
      operationId: updateUserProjectPermissions
      parameters:
        -
          name: userId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projects
              properties:
                projects:
                  type: array
                  items: { properties: { projectId: { type: integer }, permissions: { type: array, items: { type: string } } }, type: object }
              type: object
      responses:
        '204':
          description: 'No Content'
        '400':
          description: 'Validation error'
        '403':
          description: Forbidden
        '404':
          description: 'User not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/users/{userId}/project-type-permissions':
    put:
      tags:
        - User
      summary: 'Update user project-type permissions'
      description: 'Replace project-type-level permissions for a user across the listed project types.'
      operationId: updateUserProjectTypePermissions
      parameters:
        -
          name: userId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - projectTypes
              properties:
                projectTypes:
                  type: array
                  items: { properties: { projectTypeId: { type: integer }, permissions: { type: array, items: { type: string } } }, type: object }
              type: object
      responses:
        '204':
          description: 'No Content'
        '400':
          description: 'Validation error'
        '403':
          description: Forbidden
        '404':
          description: 'User not found'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{tenantId}/webhook':
    get:
      tags:
        - Webhook
      summary: 'Get tenant webhook config'
      description: 'Returns the tenant webhook config (url, enabled, secretConfigured), or null when none is configured. The secret is never returned. Accessible with WebhookIndex + tenant access.'
      operationId: getTenantWebhook
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      responses:
        '200':
          description: 'Webhook config or null'
          content:
            application/json:
              schema:
                properties:
                  url: { type: string, example: 'https://tenant.example.com/hooks/b2t' }
                  enabled: { type: boolean }
                  secretConfigured: { description: 'true when a secret is stored', type: boolean }
                type: object
                nullable: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    put:
      tags:
        - Webhook
      summary: 'Upsert tenant webhook config'
      description: 'Upserts the tenant webhook config. The secret is write-only and never returned; omit or send an empty string to keep the existing secret. Accessible with WebhookUpdate + tenant access.'
      operationId: putTenantWebhook
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - url
                - enabled
              properties:
                url:
                  type: string
                  example: 'https://tenant.example.com/hooks/b2t'
                enabled:
                  type: boolean
                secret:
                  description: 'Write-only. Omit/empty to keep existing value.'
                  type: string
                  nullable: true
              type: object
      responses:
        '204':
          description: Saved
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
    delete:
      tags:
        - Webhook
      summary: 'Delete tenant webhook config'
      description: 'Clears the tenant webhook config. Idempotent. Accessible with WebhookUpdate + tenant access.'
      operationId: deleteTenantWebhook
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      responses:
        '204':
          description: Cleared
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{tenantId}/webhook/preview':
    post:
      tags:
        - Webhook
      summary: 'Preview tenant webhook payloads'
      description: 'Builds the outbound payloads for the given platform, languages and story and returns them WITHOUT sending. Uses the same builder as send, so the preview is 1:1 with what would be delivered. Accessible with WebhookSend + tenant access.'
      operationId: previewTenantWebhook
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - platform
                - languages
                - storyName
              properties:
                platform:
                  type: string
                  example: web
                languages:
                  type: array
                  items: { type: string }
                  example: [en-US, fr]
                storyName:
                  type: string
                  example: production
              type: object
      responses:
        '200':
          description: 'Payload previews'
          content:
            application/json:
              schema:
                properties:
                  previews: { type: array, items: { properties: { language: { type: string, example: en-US }, payload: { type: object } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/tenants/{tenantId}/webhook/send':
    post:
      tags:
        - Webhook
      summary: 'Send tenant webhook payloads'
      description: 'Builds the outbound payloads (same builder as preview), signs each with HMAC-SHA256 and POSTs it to the configured tenant URL synchronously. Returns a per-language result. Accessible with WebhookSend + tenant access.'
      operationId: sendTenantWebhook
      parameters:
        -
          name: tenantId
          in: path
          description: 'Tenant UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 0c62ac53-56e1-43c1-bdcc-fcc7b9c3c8e9
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - platform
                - languages
                - storyName
              properties:
                platform:
                  type: string
                  example: web
                languages:
                  type: array
                  items: { type: string }
                  example: [en-US, fr]
                storyName:
                  type: string
                  example: production
              type: object
      responses:
        '200':
          description: 'Per-language delivery results'
          content:
            application/json:
              schema:
                properties:
                  results: { type: array, items: { properties: { language: { type: string, example: en-US }, ok: { type: boolean }, status: { type: integer, example: 200 }, error: { type: string, nullable: true } }, type: object } }
                type: object
        '400':
          description: 'Validation error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorObject'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/roles/{roleId}/assign':
    post:
      tags:
        - Workspace/Role
      summary: 'Assign a custom workspace role to users'
      description: 'Inserts `auth_assignment` rows linking the given users to the role. Users that already have the role are silently skipped. Each user must belong to the workspace — cross-workspace user ids are rejected with 400.'
      operationId: assignWorkspaceRole
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: roleId
          in: path
          description: '`auth_item.name` of the custom role'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                userIds:
                  type: array
                  items: { type: integer, example: 12 }
              type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                properties:
                  usersCount: { type: integer, example: 3 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/roles':
    get:
      tags:
        - Workspace/Role
      summary: 'List system + custom roles of a workspace'
      description: 'Returns system roles (Owner, Administrator, ...) AND every custom role defined for this workspace. `id` is `auth_item.name`. For system roles `name` = the system name; for custom roles `name` = the workspace-defined display name.'
      operationId: getWorkspaceRoles
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'List of roles'
          content:
            application/json:
              schema:
                properties:
                  roles: { type: array, items: { properties: { id: { type: string, example: Owner }, name: { type: string, example: Owner }, isSystem: { type: boolean, example: true }, priority: { type: integer, example: 800 }, permissions: { type: array, items: { type: string, example: SourceIndex } }, usersCount: { type: integer, example: 3 } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
      security:
        -
          BearerAuthObject: []
    post:
      tags:
        - Workspace/Role
      summary: 'Create a custom workspace role'
      description: 'Creates a new RBAC role scoped to the given workspace. `displayName` is the human-readable label (case-insensitive unique per workspace, max 120 chars). `permissions` must be a subset of the catalog returned by `GET /api/v2/permissions/catalog`.'
      operationId: createWorkspaceRole
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                displayName:
                  type: string
                  example: 'QA Lead'
                priority:
                  description: 'Custom role priority in [1..699] excluding reserved system slots (100,200,300,400,500,600). Must be strictly less than the caller priority. Defaults to 101.'
                  type: integer
                  example: 250
                permissions:
                  type: array
                  items: { type: string, example: SourceIndex }
              type: object
      responses:
        '200':
          description: 'Role created'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: wsr_67890abc }
                  name: { type: string, example: 'QA Lead' }
                  isSystem: { type: boolean, example: false }
                  priority: { type: integer, example: 250 }
                  permissions: { type: array, items: { type: string, example: SourceIndex } }
                  usersCount: { type: integer, example: 0 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/roles/{roleId}':
    delete:
      tags:
        - Workspace/Role
      summary: 'Delete a custom workspace role'
      description: 'Deletes a CUSTOM role of the workspace. If the role is assigned to users and `force` is not `true`, returns 409. Pass `?force=true` to unassign every user first, then delete the role.'
      operationId: deleteWorkspaceRole
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: roleId
          in: path
          description: '`auth_item.name` of the custom role'
          required: true
          schema:
            type: string
        -
          name: force
          in: query
          description: 'When `true`, also unassigns currently-assigned users.'
          required: false
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
          example: 'false'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                properties:
                  deleted: { type: boolean, example: true }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Conflict — users are assigned. Use `force=true` to unassign them.'
      security:
        -
          BearerAuthObject: []
    patch:
      tags:
        - Workspace/Role
      summary: 'Update a custom workspace role'
      description: 'Updates `displayName` and/or `permissions` of a CUSTOM role of the workspace. System roles cannot be modified.'
      operationId: updateWorkspaceRole
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: roleId
          in: path
          description: '`auth_item.name` of the custom role'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                displayName:
                  type: string
                  example: 'Senior QA'
                  nullable: true
                priority:
                  description: 'Custom role priority in [1..699] excluding reserved system slots (100,200,300,400,500,600). Must rank strictly below the caller. Null = leave unchanged.'
                  type: integer
                  example: 260
                  nullable: true
                permissions:
                  type: array
                  items: { type: string, example: SourceIndex }
                  nullable: true
              type: object
      responses:
        '200':
          description: 'Updated role'
          content:
            application/json:
              schema:
                properties:
                  id: { type: string, example: wsr_67890abc }
                  name: { type: string, example: 'Senior QA' }
                  isSystem: { type: boolean, example: false }
                  priority: { type: integer, example: 260 }
                  permissions: { type: array, items: { type: string, example: SourceIndex } }
                  usersCount: { type: integer, example: 2 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '409':
          description: 'Subscription plan limit reached; the body carries a `planLimit` message'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictObject'
      security:
        -
          BearerAuthObject: []
  /api/v2/workspaces:
    get:
      tags:
        - Workspace
      summary: 'List workspaces visible to the caller'
      description: 'For cross-workspace users (root/Owner/SuperAdmin) returns every non-deleted workspace, unless `X-Workspace-Id` header narrows to a single one. Other roles see only their own workspace.'
      operationId: getWorkspaces
      responses:
        '200':
          description: 'List of workspaces'
          content:
            application/json:
              schema:
                properties:
                  workspaces: { type: array, items: { properties: { id: { type: string, format: uuid }, name: { type: string, example: B2Broker }, slug: { type: string, example: b2broker }, planCode: { type: string, example: enterprise, nullable: true }, planName: { type: string, example: Enterprise, nullable: true }, ownerEmail: { type: string, example: dkarapetyan@b2broker.com, nullable: true }, createdAt: { type: string, example: '2026-05-10 19:33:48' } }, type: object } }
                type: object
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
      security:
        -
          BearerAuthObject: []
  /api/v2/permissions/catalog:
    get:
      tags:
        - Workspace/Role
      summary: 'List permissions assignable to a custom workspace role'
      description: 'Returns the whitelist of permission keys (grouped by module) that a workspace OWNER may include when creating or updating a custom workspace role.'
      operationId: getPermissionsCatalog
      responses:
        '200':
          description: 'Catalog of allowed permissions'
          content:
            application/json:
              schema:
                properties:
                  permissions: { type: array, items: { properties: { key: { type: string, example: SourceIndex }, module: { type: string, example: Source } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/plan':
    get:
      tags:
        - Workspace
      summary: 'Get workspace plan, limits and usage'
      description: 'Returns the workspace subscription plan, every limit it carries and the current usage measured against it. Limits come from the per-workspace snapshot (`workspace_plan.limits`), falling back to the plan template only when that snapshot is empty. Every numeric cap is pooled over the WHOLE workspace, except `max_custom_stories_per_bucket` (scope `bucket`), so a published number is a number the customer can actually reach. `cap: null` means the plan sets NO ceiling — an omitted numeric key is the stored way of saying unlimited (enterprise is `{}`, and every plan omits `max_users`, so seats are unlimited unless a vendor overlay sets a ceiling). `used: null` means that usage source is not implemented yet — `ai_tokens_per_month` is now the only such cap — and NOT that usage is zero. Boolean features follow the mirror-image rule: an omitted key means ENABLED. `subscription` is present only for workspaces provisioned from a B2STORE purchase. Accessible by super roles for any workspace, and by a Workspace Administrator of this one.'
      operationId: getWorkspacePlan
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
          example: 019fc694-d096-70ab-bd16-2d68aed25400
      responses:
        '200':
          description: 'Plan, limits and usage'
          content:
            application/json:
              schema:
                properties:
                  workspaceId: { type: string, format: uuid }
                  plan: { properties: { code: { type: string, example: growth }, name: { type: string, example: Growth } }, type: object }
                  subscription: { description: 'B2STORE subscription context; null for manually created workspaces.', properties: { isTrial: { type: boolean, example: false }, lifecycleStatus: { type: string, example: active, nullable: true }, purchasedAt: { type: string, example: '2026-04-01 10:15:00+00' } }, type: object, nullable: true }
                  caps: { description: 'Numeric limits, in a fixed order.', type: array, items: { properties: { key: { type: string, example: max_tenants }, scope: { description: 'What one count is measured over: `workspace` for every cap except `max_custom_stories_per_bucket`, which is `bucket`.', type: string, example: workspace }, cap: { type: integer, example: 25, nullable: true }, used: { type: integer, example: 31, nullable: true } }, type: object } }
                  features: { description: 'Boolean plan features, in a fixed order.', type: array, items: { properties: { key: { type: string, example: sdk_cli }, enabled: { type: boolean, example: false } }, type: object } }
                  tenantsPerProduct: { description: "Where the workspace's tenants are, highest first — INFORMATION only. No cap is measured from this list: `max_tenants` counts the workspace POOL, because reporting the largest product instead is what let twenty products at their cap read as compliant.", type: array, items: { properties: { productId: { type: string, format: uuid }, productName: { type: string, example: b2core }, used: { type: integer, example: 2 } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Workspace not found, or it has no plan'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
        '500':
          description: 'Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerErrorObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/roles/{roleId}/users':
    get:
      tags:
        - Workspace/Role
      summary: 'List users assigned to a custom workspace role'
      operationId: getWorkspaceRoleUsers
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: roleId
          in: path
          description: '`auth_item.name` of the custom role'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Assigned users'
          content:
            application/json:
              schema:
                properties:
                  users: { type: array, items: { properties: { id: { type: integer, example: 12 }, email: { type: string, example: user@example.com }, fullName: { type: string, example: 'Jane Doe' } }, type: object } }
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
      security:
        -
          BearerAuthObject: []
  '/api/v2/workspaces/{id}/roles/{roleId}/unassign':
    post:
      tags:
        - Workspace/Role
      summary: 'Unassign a custom workspace role from users'
      description: 'Removes `auth_assignment` rows for the given users. Users that do not have the role are silently skipped.'
      operationId: unassignWorkspaceRole
      parameters:
        -
          name: id
          in: path
          description: 'Workspace UUID'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: roleId
          in: path
          description: '`auth_item.name` of the custom role'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                userIds:
                  type: array
                  items: { type: integer, example: 12 }
              type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                properties:
                  usersCount: { type: integer, example: 1 }
                type: object
        '400':
          description: 'Validation error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenObject'
        '404':
          description: 'Not Found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundObject'
      security:
        -
          BearerAuthObject: []
components:
  schemas:
    EmailObject:
      type: string
      example: test@mail.com
    JwtTokenObject:
      required:
        - token
        - expiresAt
      properties:
        token:
          type: string
          example: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.iJ3ZWJsYXRlM19pc3N1ZWRfYnkiLCJleHAiOjE2ODY0O.DgwOTQuNTcyNTM1LCJ1aWQiOjEsInJhbmQiOiIxNjgzODA5Njk0MTY3In0
        expiresAt:
          properties:
            date:
              type: string
              example: '2023-06-11 12:54:54.572535'
            timezone_type:
              type: integer
              example: 1
            timezone:
              type: string
              example: '+00:00'
          type: object
      type: object
    BadRequestObject:
      required:
        - errors
      properties:
        errors:
          description: "The 400 envelope for a malformed request, as opposed to one that failed field\nvalidation ({@see ValidationErrorObject}).\n\nOne of three shared error schemas that were referenced but never declared; see\n{@see ValidationErrorObject} for why an unresolved `$ref` mattered."
          type: object
          example:
            id: 'Invalid UUID format'
          additionalProperties:
            type: string
      type: object
    ConflictObject:
      required:
        - errors
      properties:
        errors:
          description: "The 409 envelope produced by `ConflictException` — the resource already exists.\n\nOne of three shared error schemas that were referenced but never declared; see\n{@see ValidationErrorObject} for why an unresolved `$ref` mattered."
          type: object
          example:
            langBcp: 'Language already exists'
          additionalProperties:
            type: string
      type: object
    ForbiddenObject:
      required:
        - message
      properties:
        message:
          properties:
            error:
              type: string
              example: 'You are not allowed to perform this action.'
          type: object
      type: object
    NotFoundObject:
      required:
        - message
      properties:
        message:
          properties:
            error:
              type: string
              example: 'Page not found'
          type: object
      type: object
    ServerErrorObject:
      required:
        - message
      properties:
        message:
          properties:
            error:
              type: string
              example: 'Oops! Something went wrong! Write to administrator. Your Request Id {f1177aee-9a99-42f0-a23f-4dd29fc39b65-672e0df972ccb}'
          type: object
      type: object
    UnauthorizedObject:
      required:
        - message
      properties:
        message:
          properties:
            error:
              type: string
              example: 'Your request was made with invalid credentials.'
          type: object
      type: object
    ValidationErrorObject:
      required:
        - errors
      properties:
        errors:
          description: "The 400 envelope produced by `ValidationException` — a map of field name to message.\n\nAdded because the schema was referenced by six controllers (Language, Webhook and\nNotification) but never declared, which made `composer openapi` abort on an\nunresolved `$ref` and silently write nothing: the command still exited 0, so the\ncommitted spec quietly went stale instead of failing the build.\n\nThe shape follows what `ErrorHandler` actually emits for modules outside\n`getNewModules()`: `{\"errors\": {\"field\": \"message\"}}`. Modules listed there wrap it\nin `message` instead, which is why this is documented per-endpoint rather than\nglobally."
          type: object
          example:
            name: 'Empty value'
          additionalProperties:
            type: string
      type: object
  securitySchemes:
    BearerAuthObject:
      type: http
      bearerFormat: JWT
      scheme: bearer
tags:
  -
    name: AiTranslator
    description: AiTranslator
  -
    name: Auth
    description: Auth
  -
    name: B2Store
    description: B2Store
  -
    name: 'Backoffice Tenant'
    description: 'Backoffice Tenant'
  -
    name: Backoffice
    description: Backoffice
  -
    name: Cache
    description: Cache
  -
    name: Categories
    description: Categories
  -
    name: Glossary
    description: Glossary
  -
    name: Intercom
    description: Intercom
  -
    name: Language
    description: Language
  -
    name: Notification
    description: Notification
  -
    name: PageInformation
    description: PageInformation
  -
    name: Queue
    description: Queue
  -
    name: Story
    description: Story
  -
    name: Tenant/Product
    description: Tenant/Product
  -
    name: Tenant/Buckets
    description: Tenant/Buckets
  -
    name: 'Product Robot Tokens'
    description: 'Product Robot Tokens'
  -
    name: Tenant
    description: Tenant
  -
    name: Tenant/Platforms
    description: Tenant/Platforms
  -
    name: 'Tenant/Project Type'
    description: 'Tenant/Project Type'
  -
    name: Tenant/Tenant
    description: Tenant/Tenant
  -
    name: Translation
    description: Translation
  -
    name: Key
    description: Key
  -
    name: Robot
    description: Robot
  -
    name: User/Account
    description: User/Account
  -
    name: 'Personal API Tokens'
    description: 'Personal API Tokens'
  -
    name: User
    description: User
  -
    name: User/Role
    description: User/Role
  -
    name: Webhook
    description: Webhook
  -
    name: Workspace/Role
    description: Workspace/Role
  -
    name: Workspace
    description: Workspace
