openapi: 3.0.3
info:
  title: Taliware's CL2C API
  version: 1.0.0
  description: Comprehensive API for managing organizations, users, NFTs, minting, and ownership confirmations (CL2C surface).
servers:
  - url: https://developer.taliware.com/api/v1

components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
    apiKeyBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
    cookieAuth:
      type: apiKey
      in: cookie
      name: next-auth.session-token

  schemas:

    # Generic error shape used across endpoints
    Error:
      type: object
      description: Standard error payload
      properties:
        error:
          type: string
        details:
          type: array
          items:
            type: string

    # Simple pagination envelope used by list endpoints
    Pagination:
      type: object
      properties:
        page:
          type: integer
          description: Current page (1-indexed)
        limit:
          type: integer
          description: Items per page
        total:
          type: integer
          description: Total number of matching records
        totalPages:
          type: integer
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

    PaginatedResponse:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
        pagination:
          $ref: '#/components/schemas/Pagination'
        filters:
          type: object
          additionalProperties: true

    # Contract
    Contract:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        symbol:
          type: string
        description:
          type: string
        network:
          type: string
        env:
          type: string
        status:
          type: string
          enum: [CREATED, DEPLOYED, FAILED]
        contractAddress:
          type: string
        deployedAt:
          type: string
          format: date-time
        metadata:
          type: object
        totalNFTs:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    ContractCreate:
      type: object
      required: [name, symbol]
      properties:
        name:
          type: string
        symbol:
          type: string
        description:
          type: string
        network:
          type: string
          enum: [POLYGON]
        env:
          type: string
          enum: [TESTNET, MAINNET]
        metadata:
          type: object

    ContractUpdate:
      type: object
      required: [id]
      properties:
        id:
          type: string
        status:
          type: string
          enum: [CREATED, DEPLOYED, FAILED]
        contractAddress:
          type: string
        deployedAt:
          type: string
          format: date-time

    ContractDelete:
      type: object
      required: [contractId]
      properties:
        contractId:
          type: string

    # User
    CreateUser:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        phone:
          type: string
          nullable: true

    BulkUserEntry:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        phone:
          type: string
          nullable: true

    BulkCreateUser:
      type: object
      required:
        - users
      properties:
        users:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/BulkUserEntry'

    BulkUserResult:
      type: object
      properties:
        email:
          type: string
          format: email
        status:
          type: string
          enum: [invited, skipped, failed]
        reason:
          type: string
          nullable: true
          description: Present when status is skipped or failed.
        id:
          type: string
          nullable: true
          description: Invitation ID, present when status is invited.

    BulkCreateUserResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/BulkUserResult'
            summary:
              type: object
              properties:
                total:
                  type: integer
                  description: Number of unique emails processed.
                invited:
                  type: integer
                skipped:
                  type: integer
                  description: Already a member or pending invitation.
                failed:
                  type: integer
                  description: Unexpected errors during invitation creation.

    UserOrgSummary:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        walletAddress:
          type: string
          nullable: true
        totalNFTs:
          type: integer
        status:
          type: string
          enum: [ACTIVE, INVITED]
        createdAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time
          nullable: true
        user:
          $ref: '#/components/schemas/UserInfo'

    UserOrgItem:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/UserOrgSummary'

    UserInfo:
      type: object
      properties:
        id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        image:
          type: string
          nullable: true
        emailVerified:
          type: boolean
          nullable: true
        isRegistered:
          type: boolean
   
    UpdateUser:
      type: object
      properties:
        email:
          type: string
          format: email
        resend:
          type: boolean
          description: Resend invitation (only for pending invitations)

    UserOrgDetail:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        walletAddress:
          type: string
          nullable: true
        user:
          type: object
          properties:
            name:
              type: string
              nullable: true
            image:
              type: string
              nullable: true
        endUserNfts:
          type: array
          items:
            type: object
            properties:
              status:
                type: string
              percentage:
                type: number
              copyAuthLocation:
                type: string
                nullable: true
              copyAuthTimestamp:
                type: string
                format: date-time
                nullable: true
              nft:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  mintedAt:
                    type: string
                    format: date-time
                  supply:
                    type: integer
                  name:
                    type: string
                  description:
                    type: string
                    nullable: true
                  externalUrl:
                    type: string
                    nullable: true
                  imagePath:
                    type: string
                    nullable: true
                  smartContract:
                    type: object
                    properties:
                      name:
                        type: string
                      symbol:
                        type: string
                      contractAddress:
                        type: string
        status:
          type: string
          enum: [ACTIVE, INVITED]

    UserOrgUpdateResponse:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        walletAddress:
          type: string
          nullable: true
   
    # NFT
    MintRequest:
        type: object
        required: [name, smartContractId, copyright]
        properties:
          name:
            type: string
          description:
            type: string
          externalUrl:
            type: string
          imagePath:
            type: string
            description: >
              Optional. ipfs:// URI, data URI, raw base64, http(s) URL, or local path for
              the asset's image. If omitted, the NFT is created as traceability-only —
              an on-chain record and metadata JSON are created with no IPFS file upload.
          metadata:
            type: object
          smartContractId:
            type: string
          supply:
            type: integer
          copyright:
            type: array
            items:
              type: object
              required: [userId, percentage]
              properties:
                userId:
                  type: string
                percentage:
                  type: number

    UpdateNFT:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        status:
          type: string
          # enum: [PENDING, CREATED, MINTED, ATTACHED, FAILED]
          enum: [PENDING, CREATED, MINTED, FAILED]
        mintedAt:
          type: string
          format: date-time

    DeleteNFTRequest:
      type: object
      properties:
        nftId:
          type: string

    NFTSummary:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
        name:
          type: string
        description:
          type: string
        externalUrl:
          type: string
        imagePath:
          type: string
        supply:
          type: integer
        mintedAt:
          type: string
          format: date-time
        smartContract:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            symbol:
              type: string
            contractAddress:
              type: string
            status:
              type: string
            network:
              type: string
            env:
              type: string

    NFTDetail:
      allOf:
        - $ref: '#/components/schemas/NFTSummary'
        - type: object
          properties:
            copyright:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  percentage:
                    type: number
                  email:
                    type: string
                  walletAddress:
                    type: array
                    nullable: true
                    items:
                      type: object
                      properties:
                        address:
                          type: string
                        isPrimary:
                          type: boolean
                  userName:
                    type: string
                    nullable: true
                  userImage:
                    type: string
                    nullable: true
            totalOwners:
              type: integer

    UpdateNFTbyID:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        externalUrl:
          type: string
          format: uri
        supply:
          type: integer
          minimum: 1
        metadata:
          type: object
          additionalProperties: true

    TokenBalanceResponse:
      type: object
      properties:
        balance:
          type: number
        walletAddress:
          type: string
          nullable: true

    TokenBalancesResponse:
      type: object
      properties:
        mainnet:
          $ref: '#/components/schemas/TokenBalanceResponse'
        faucet:
          allOf:
            - $ref: '#/components/schemas/TokenBalanceResponse'
          nullable: true

    TokenSendRequest:
      type: object
      required: [memberId, amount]
      properties:
        memberId:
          type: string
        amount:
          type: number
          minimum: 0
          exclusiveMinimum: true
        tokenType:
          type: string
          enum: [mainnet, faucet]
          default: mainnet

    TokenSendResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        transactionHash:
          type: string
          nullable: true
        tokenType:
          type: string
          enum: [mainnet, faucet]

    StripeCheckoutRequest:
      type: object
      required: [amount]
      properties:
        amount:
          type: number
          minimum: 1

    StripeCheckoutResponse:
      type: object
      properties:
        sessionId:
          type: string
        checkoutUrl:
          type: string

paths:

  # Contracts
  /cl2c/contract:
    get:
      tags:
        - Contract
      summary: List smart contracts for the authenticated organization
      description: Retrieve smart contract records belonging to the organization identified by the X-API-KEY header.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
            enum: [CREATED, DEPLOYED, FAILED]
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: Array of contracts with pagination info
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Contract'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - Contract
      summary: Create a new smart contract record (not deployed yet)
      description: Create a smart contract entry in the system (deployment is a separate action).
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractCreate'
      responses:
        '201':
          description: Contract created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Contract already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - Contract
      summary: Update a smart contract
      description: Update status, contractAddress, or deployedAt of an existing contract.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractUpdate'
      responses:
        '200':
          description: Contract updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - Contract
      summary: Delete a smart contract
      description: Delete a contract if it is not deployed and has no associated NFTs.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractDelete'
      responses:
        '200':
          description: Contract deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedContract:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request / cannot delete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Contracts by ID
  /cl2c/contract/{id}:
    put:
      tags:
        - Contract
      summary: Deploy smart contract (trigger on-chain deploy)
      description: Triggers on-chain contract deployment for the specified contract record. Only works if the contract is not already deployed.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the contract to deploy
      responses:
        '200':
          description: Contract deployed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request (e.g., invalid ID or contract already deployed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - Contract
      summary: Delete a smart contract record
      description: Remove a smart contract record from the system. Only allowed if no associated NFTs exist and the contract is not deployed.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the contract to delete
      responses:
        '200':
          description: Contract deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedContract:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request (contract deployed or has associated NFTs)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Users (organization-scoped)
  /cl2c/user:
    get:
      tags:
        - User
      summary: List users (userOrganization records) in the authenticated organization
      description: Returns a paginated list of userOrganization entries (members + pending invitations) for the organization tied to the X-API-KEY.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
        - in: query
          name: hasWallet
          schema:
            type: boolean
      responses:
        '200':
          description: Paginated list of userOrganization records
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/UserOrgSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
                    additionalProperties: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - User
      summary: Invite (create) a userOrganization record for this organization
      description: Create an invited (or existing) userOrganization for the organization. If the email matches a registered user, userId will be linked automatically.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '201':
          description: Created userOrganization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgItem'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict (already exists)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Bulk user invite
  /cl2c/user/bulk:
    post:
      tags:
        - User
      summary: Bulk invite users to the organization
      description: |
        Invite up to 100 users in a single request. Each entry is processed independently.
        Returns HTTP 207 (Multi-Status) with per-user results. Duplicate emails within the
        request are deduplicated automatically. Emails already a member or with a pending
        invitation are reported as `skipped`, not `failed`.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkCreateUser'
            example:
              users:
                - email: alice@example.com
                - email: bob@example.com
                  phone: "+1 555 000 0001"
      responses:
        '207':
          description: Multi-status — individual results per email address
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkCreateUserResponse'
        '400':
          description: Bad request (invalid body, exceeds 100-user limit, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Organization not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # User by ID
  /cl2c/user/{id}:
    get:
      tags:
        - User
      summary: Get a specific userOrganization by id (admin/org-scoped)
      description: Retrieve a userOrganization entry plus linked user and NFT ownership info. Includes pending invitations if user not yet registered.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      responses:
        '200':
          description: userOrganization detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgDetail'
        '400':
          description: Bad request (invalid ID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found (user or invitation does not exist)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - User
      summary: Update a user or invitation email
      description: Update email for a member or invitation. Resend is only allowed for invitations.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUser'
      responses:
        '200':
          description: Updated userOrganization or invitation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgUpdateResponse'
        '400':
          description: Bad request (invalid body, resend for member, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict (duplicate email or wallet)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - User
      summary: Delete a userOrganization by id (admin)
      description: Delete a userOrganization or pending invitation. Users with wallet addresses or NFTs cannot be deleted.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      responses:
        '200':
          description: Deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Bad request (user has wallet or NFTs)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # NFT collection
  /cl2c/nft:
    get:
      tags:
        - NFT
      summary: List NFTs for the authenticated organization
      description: Returns NFTs for the organization's smart contracts. Supports search, status, contractId filters, and pagination.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
          description: Search NFTs by name or description
        - in: query
          name: status
          schema:
            type: string
          description: Filter by NFT status (CREATED, PENDING, MINTED, FAILED)
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: Paginated list of NFTs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/NFTSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
                    additionalProperties: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - NFT
      summary: Create (mint) a new NFT
      description: Creates NFT row with status=CREATED and pending userNft entries for owners; may upload image to IPFS and send notifications.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MintRequest'
      responses:
        '201':
          description: NFT created (userNft entries created and notifications sent)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTDetail'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Related resource not found (e.g., smart contract or user)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - NFT
      summary: Update NFT metadata or status
      description: Update name, description, status, or mintedAt for an NFT.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNFT'
      responses:
        '200':
          description: NFT updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTSummary'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found in organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - NFT
      summary: Delete an NFT
      description: Delete an NFT and all its pending userNft entries. Minted NFTs cannot be deleted.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeleteNFTRequest'
      responses:
        '200':
          description: NFT deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedNft:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request (e.g., cannot delete minted NFT)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # NFT collection by ID
  /cl2c/nft/{id}:
    get:
      tags:
        - NFT
      summary: Get NFT detail (includes copyright entries and per-owner info)
      description: Retrieve a single NFT and its copyright/userNft entries. Ensures NFT belongs to the authenticated organization.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: NFT ID
      responses:
        '200':
          description: NFT detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTDetail'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - NFT
      summary: Update NFT fields (name, description, status, mintedAt)
      description: Update editable NFT fields. Only allowed when NFT status is not finalized (CREATED or PENDING). Status transitions are validated server-side.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNFTbyID'
      responses:
        '200':
          description: Updated NFT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTSummary'
        '400':
          description: Bad request / invalid state (e.g., NFT already MINTED or ATTACHED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to your organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - NFT
      summary: Delete an NFT
      description: Delete an NFT record and its copyright entries. Only allowed when NFT status is not MINTED or ATTACHED.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Bad request (e.g., cannot delete minted NFT)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to your organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Token balance and transfer
  /cl2c/token:
    get:
      tags:
        - Tokens
      summary: Get owner token balances
      description: Returns owner token balance for a requested type, or both mainnet and faucet when type is omitted.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
        - cookieAuth: []
      parameters:
        - in: query
          name: type
          required: false
          schema:
            type: string
            enum: [mainnet, faucet]
          description: When provided, returns a single balance object. If omitted, returns both balances.
      responses:
        '200':
          description: Token balance response
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TokenBalanceResponse'
                  - $ref: '#/components/schemas/TokenBalancesResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - Tokens
      summary: Send tokens to an organization member
      description: Requires owner membership in the authenticated organization.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenSendRequest'
      responses:
        '200':
          description: Tokens sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenSendResponse'
        '400':
          description: Invalid request body or wallet configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only owners can send tokens
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Recipient or member not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server or token transfer error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
