openapi: 3.1.0
info:
  title: Nasak Support API
  version: 1.0.0-alpha.2
  description: Tenant-safe support tickets, rich messages, private attachments, and administrative inbox operations.
servers:
  - url: https://api.nasak.ir/v1/support
paths:
  /tickets:
    get:
      operationId: listMySupportTickets
      summary: List tickets visible to the current user
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createMySupportTicket
      summary: Create a main-site or store-scoped support ticket
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility mutation with same-origin, rate-limit, sanitization, and audit controls.
      requestBody: { $ref: '#/components/requestBodies/Mutation' }
      responses:
        '201': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /tickets/{id}:
    parameters: [{ $ref: '#/components/parameters/Id' }]
    get:
      operationId: getMySupportTicket
      summary: Get an accessible ticket, safe messages, and attachment metadata
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      operationId: updateMySupportTicket
      summary: Close or reopen a ticket owned by the current user
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Resource-state compatibility mutation.
      requestBody: { $ref: '#/components/requestBodies/Mutation' }
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /tickets/{id}/messages:
    post:
      operationId: replyToMySupportTicket
      summary: Add a sanitized plain or limited-HTML message
      parameters: [{ $ref: '#/components/parameters/Id' }]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility mutation with server-side HTML sanitization.
      requestBody: { $ref: '#/components/requestBodies/Mutation' }
      responses:
        '201': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /tickets/{id}/attachments:
    post:
      operationId: uploadSupportAttachment
      summary: Upload a private validated image or PDF attachment
      parameters: [{ $ref: '#/components/parameters/Id' }]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Multipart upload is checksum-verified, access-controlled, and rate-limited.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema: { type: object, additionalProperties: true }
      responses:
        '201': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '413': { description: Attachment is too large. }
        '415': { description: Attachment type or magic bytes are invalid. }
  /attachments/{id}:
    get:
      operationId: downloadSupportAttachment
      summary: Download an attachment after ticket access verification
      parameters: [{ $ref: '#/components/parameters/Id' }]
      security: [{ cookieSession: [] }]
      responses:
        '200': { description: Private attachment stream with nosniff headers. }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/tickets:
    get:
      operationId: listAdminSupportTickets
      summary: Search the authorized support inbox across permitted scopes
      security:
        - cookieSession: []
        - oauth2: [support.tickets.admin.read]
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/tickets/{id}:
    parameters: [{ $ref: '#/components/parameters/Id' }]
    get:
      operationId: getAdminSupportTicket
      summary: Get an authorized ticket including internal management context
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      operationId: updateAdminSupportTicket
      summary: Assign, prioritize, or transition a ticket
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Audited compatibility management mutation.
      requestBody: { $ref: '#/components/requestBodies/Mutation' }
      responses:
        '200': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/tickets/{id}/messages:
    post:
      operationId: replyToAdminSupportTicket
      summary: Add a support reply or internal-only note
      parameters: [{ $ref: '#/components/parameters/Id' }]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Audited and sanitized compatibility mutation.
      requestBody: { $ref: '#/components/requestBodies/Mutation' }
      responses:
        '201': { $ref: '#/components/responses/Success' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
components:
  securitySchemes:
    cookieSession: { type: apiKey, in: cookie, name: pdf_library_session }
    oauth2:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://auth.nasak.ir/realms/nasak/protocol/openid-connect/token
          scopes:
            support.tickets.admin.read: Read the administrative support ticket inbox.
  parameters:
    Id:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
  requestBodies:
    Mutation:
      required: true
      content:
        application/json:
          schema: { type: object, additionalProperties: true }
  schemas:
    Error:
      type: object
      additionalProperties: true
      required: [error]
      properties: { error: { type: string } }
  responses:
    Success:
      description: Successful tenant-scoped response.
      content:
        application/json:
          schema: { type: object, additionalProperties: true }
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Unauthorized:
      description: Authentication is required.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Forbidden:
      description: Ticket or store access is denied.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    TooManyRequests:
      description: Rate limit exceeded.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
