# yaml-language-server: $$schema=https://json.schemastore.org/swagger-2.0.json
swagger: "2.0"
info:
  title: PlantCare Unified API
  version: 1.1.0
schemes:
  - https
produces:
  - application/json

# Enable global CORS via x-google-endpoints (Task 161)
x-google-endpoints:
  - name: "api.plantcare.farm"
    allowCors: true

# Security Definitions for JWT (Mobile) and API Key (Enterprise)
securityDefinitions:
  firebase_auth:
    type: "oauth2"
    authorizationUrl: ""
    flow: "implicit"
    scopes: {}
    x-google-issuer: "https://securetoken.google.com/${GCP_PROJECT}"

    x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"
    x-google-audiences: "${GCP_PROJECT}"
  api_key:
    type: "apiKey"
    name: "x-api-key"
    in: "header"

paths:

  # ===========================================================================
  # --- VERSION 1 (RESTful Standard) ---
  # ===========================================================================

  # --- Worker Invitations (OTP) ---

  /v1/invitations/invite:
    options:
      summary: CORS preflight
      operationId: cors_v1invitationsinvite
      x-google-backend:
        address: ${INVITE_ENTERPRISE_WORKER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Send worker invitation
      operationId: v1InviteEnterpriseWorker
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${INVITE_ENTERPRISE_WORKER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/invitations/verify:
    options:
      summary: CORS preflight
      operationId: cors_v1invitationsverify
      x-google-backend:
        address: ${VERIFY_INVITATION_OTP_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Verify worker invitation OTP
      operationId: v1VerifyInvitationOtp
      x-google-backend:
        address: ${VERIFY_INVITATION_OTP_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/invitations/redeem:
    options:
      summary: CORS preflight
      operationId: cors_v1invitationsredeem
      x-google-backend:
        address: ${REDEEM_INVITATION_OTP_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Redeem worker invitation OTP
      operationId: v1RedeemInvitationOtp
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${REDEEM_INVITATION_OTP_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  # --- IoT Devices ---

  /v1/iot/devices:
    options:
      summary: CORS preflight
      operationId: cors_v1iotdevices
      x-google-backend:
        address: ${LIST_IOT_DEVICES_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: List all IoT devices for the account
      operationId: v1ListIotDevices
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${LIST_IOT_DEVICES_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK
    post:
      summary: Register a new IoT device
      operationId: v1RegisterIotDevice
      security:
        - firebase_auth: []
        - api_key: []
      parameters:
        - name: body
          in: body
          required: true
          schema:
            type: object
            required:
              - type
              - projectId
              - placeId
            properties:
              id: { type: string }
              type: { type: string, enum: [sensor, camera, drone] }
              projectId: { type: string }
              placeId: { type: string }
              plantIds:
                type: array
                items: { type: string }
              metadata:
                type: object
      x-google-backend:
        address: ${REGISTER_IOT_DEVICE_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/iot/devices/{id}:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1iotdevicesid
      x-google-backend:
        address: ${UPDATE_IOT_DEVICE_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    patch:
      summary: Update assignment or metadata of an IoT device
      operationId: v1UpdateIotDevice
      security:
        - firebase_auth: []
        - api_key: []
      parameters:
        - name: id
          in: path
          required: true
          type: string
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              type: { type: string, enum: [sensor, camera, drone] }
              projectId: { type: string }
              placeId: { type: string }
              plantIds:
                type: array
                items: { type: string }
              metadata:
                type: object
      x-google-backend:
        address: ${UPDATE_IOT_DEVICE_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK
    delete:
      summary: Decommission/Unregister an IoT device
      operationId: v1DeleteIotDevice
      security:
        - firebase_auth: []
        - api_key: []
      parameters:
        - name: id
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${DELETE_IOT_DEVICE_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  # --- Analyses ---

  /v1/analyses:
    options:
      summary: CORS preflight
      operationId: cors_v1analyses
      x-google-backend:
        address: ${START_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Start asynchronous plant analysis transaction (Mobile Preferred)
      operationId: v1StartAnalysis
      security:
        - firebase_auth: []
      parameters:
        - name: Idempotency-Key
          in: header
          type: string
          description: A unique UUID v4 to ensure request idempotency and prevent duplicate executions.
          required: false
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              storagePath: { type: string }
              imageBase64: { type: string }
              language: { type: string }
              latitude: { type: number, format: float }
              longitude: { type: number, format: float }
              placement: { type: string, enum: [inside, outside] }
      x-google-backend:
        address: ${START_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: Successful initiation.
          schema:
            type: object
            properties:
              transactionId:
                type: string
              timeoutSeconds:
                type: integer

  /v1/analyses/sync:
    options:
      summary: CORS preflight
      operationId: cors_v1analysessync
      x-google-backend:
        address: ${GET_PLANT_CARE_ADVICE_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Analyze plant via image (Synchronous/Direct)
      operationId: v1SyncAnalyze
      security:
        - firebase_auth: []
        - api_key: []
      parameters:
        - name: Idempotency-Key
          in: header
          type: string
          description: A unique UUID v4 to ensure request idempotency and prevent duplicate executions.
          required: false
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              imageBase64: { type: string }
              sunExposure: { type: string }
              wateringFrequency: { type: integer }
              language: { type: string }
              latitude: { type: number, format: float }
              longitude: { type: number, format: float }
              placement: { type: string, enum: [inside, outside] }
      x-google-backend:
        address: ${GET_PLANT_CARE_ADVICE_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/analyses/save:
    options:
      summary: CORS preflight
      operationId: cors_v1analysessave
      x-google-backend:
        address: ${SAVE_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Save a temporary analysis to permanent collection
      operationId: v1SaveAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${SAVE_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/analyses/{id}:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1analysesid
      x-google-backend:
        address: ${DELETE_ANALYSIS_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    delete:
      summary: Delete an analysis record
      operationId: v1DeleteAnalysis
      parameters:
        - name: id
          in: path
          required: true
          type: string
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${DELETE_ANALYSIS_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  # --- History ---

  /v1/history:
    options:
      summary: CORS preflight
      operationId: cors_v1history
      x-google-backend:
        address: ${GET_ANALYSIS_HISTORY_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: Retrieve analysis history
      operationId: v1GetHistory
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${GET_ANALYSIS_HISTORY_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/history/{id}/impression:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1historyidimpression
      x-google-backend:
        address: ${UPDATE_ANALYSIS_IMPRESSION_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    put:
      summary: Update feedback (up/down) for an analysis
      operationId: v1UpdateImpression
      security:
        - firebase_auth: []
        - api_key: []
      parameters:
        - name: id
          in: path
          required: true
          type: string
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              impression:
                type: string
                enum: [up, down, ""]
      x-google-backend:
        address: ${UPDATE_ANALYSIS_IMPRESSION_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  # --- Folders ---

  /v1/folders:
    options:
      summary: CORS preflight
      operationId: cors_v1folders
      x-google-backend:
        address: ${CREATE_FOLDER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Create a history folder
      operationId: v1CreateFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${CREATE_FOLDER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/folders/{id}:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1foldersid
      x-google-backend:
        address: ${DELETE_FOLDER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    delete:
      summary: Delete a history folder
      operationId: v1DeleteFolder
      parameters:
        - name: id
          in: path
          required: true
          type: string
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${DELETE_FOLDER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/folders/{id}/assign:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1foldersidassign
      x-google-backend:
        address: ${ASSIGN_TO_FOLDER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Assign analyses to a folder
      operationId: v1AssignToFolder
      parameters:
        - name: id
          in: path
          required: true
          type: string
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ASSIGN_TO_FOLDER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/folders/unassign:
    options:
      summary: CORS preflight
      operationId: cors_v1foldersunassign
      x-google-backend:
        address: ${UNASSIGN_FROM_FOLDER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Remove analyses from their current folder
      operationId: v1UnassignFromFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${UNASSIGN_FROM_FOLDER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  # --- Session & Account ---

  /v1/session:
    options:
      summary: CORS preflight
      operationId: cors_v1session
      x-google-backend:
        address: ${START_SESSION_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Initialize user session
      operationId: v1StartSession
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${START_SESSION_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/stats:
    options:
      summary: CORS preflight
      operationId: cors_v1stats
      x-google-backend:
        address: ${GET_STATS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: Get account usage statistics
      operationId: v1GetStats
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${GET_STATS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/gdpr/export:
    options:
      summary: CORS preflight
      operationId: cors_v1gdprexport
      x-google-backend:
        address: ${REQUEST_DATA_EXPORT_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Trigger GDPR data export
      operationId: v1RequestDataExport
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${REQUEST_DATA_EXPORT_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/config:
    options:
      summary: CORS preflight
      operationId: cors_v1config
      x-google-backend:
        address: ${GET_REMOTE_CONFIG_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: Retrieve unauthenticated remote system configuration
      operationId: v1GetConfig
      security: [] # Public endpoint
      x-google-backend:

        address: ${GET_REMOTE_CONFIG_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 10.0
      responses:
        "200":
          description: OK

  /v1/health:
    options:
      summary: CORS preflight
      operationId: cors_v1health
      x-google-backend:
        address: ${HEALTHZ_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: Health check endpoint (unauthenticated)
      operationId: v1health
      security: [] # Public endpoint
      x-google-backend:
        address: ${HEALTHZ_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
        "503":
          description: Service Unavailable

  /v1/status:
    options:
      summary: CORS preflight
      operationId: cors_v1status
      x-google-backend:
        address: ${GET_SYSTEM_STATUS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: Aggregate system status (public)
      operationId: v1GetSystemStatus
      security: [] # Public endpoint
      x-google-backend:
        address: ${GET_SYSTEM_STATUS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 15.0
      responses:
        "200":
          description: OK

  # --- Enterprise Auth: Token Exchange ---

  /v1/auth/exchange-token:
    options:
      summary: CORS preflight
      operationId: cors_v1authexchangetoken
      x-google-backend:
        address: ${EXCHANGE_API_TOKEN_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Exchange a pk_ent_ token for a short-lived Firebase ID token
      description: |
        B2B customers exchange their enterprise API token (pk_ent_) for a
        short-lived Firebase ID token (1 hour). The ID token is then used
        for all API calls through the API Gateway. This is the OAuth2
        client credentials pattern.

        The pk_ent_ token must be sent in the X-Enterprise-Token header.
        The API Gateway replaces the Authorization header with its own OIDC
        token, so the raw pk_ent_ token cannot be sent via Authorization.
      operationId: v1ExchangeApiToken
      security: [] # Public endpoint — backend verifies the pk_ent_ token directly
      x-google-backend:
        address: ${EXCHANGE_API_TOKEN_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 15.0
      parameters:
        - in: header
          name: X-Enterprise-Token
          description: The raw pk_ent_ enterprise API token
          required: true
          type: string
      responses:
        "200":
          description: OK
        "401":
          description: Unauthorized — invalid or revoked enterprise token

  # --- Enterprise Tokens & Webhooks ---

  /v1/tokens:
    options:
      summary: CORS preflight
      operationId: cors_v1tokens
      x-google-backend:
        address: ${GENERATE_API_TOKEN_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Generate a new API key
      operationId: v1GenerateToken
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${GENERATE_API_TOKEN_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK
    get:
      summary: List active API tokens for the authenticated account
      operationId: v1ListTokens
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${LIST_API_TOKENS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 15.0
      responses:
        "200":
          description: OK

  /v1/tokens/{id}:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1tokensid
      x-google-backend:
        address: ${REVOKE_API_TOKEN_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    delete:
      summary: Revoke an existing API key
      operationId: v1RevokeToken
      parameters:
        - name: id
          in: path
          required: true
          type: string
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${REVOKE_API_TOKEN_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/tokens/{id}/rotate:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1tokensidrotate
      x-google-backend:
        address: ${ROTATE_API_TOKEN_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Rotate an API token — mints a new token, revokes the old one
      operationId: v1RotateToken
      parameters:
        - name: id
          in: path
          required: true
          type: string
      security:
        - firebase_auth: []
        - api_key: []
      x-google-backend:
        address: ${ROTATE_API_TOKEN_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/webhooks:
    options:
      summary: CORS preflight
      operationId: cors_v1webhooks
      x-google-backend:
        address: ${REGISTER_WEBHOOK_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Register an enterprise callback URL
      operationId: v1RegisterWebhook
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${REGISTER_WEBHOOK_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/webhooks/gemini:
    options:
      summary: CORS preflight
      operationId: cors_v1webhooksgemini
      x-google-backend:
        address: ${GEMINI_WEBHOOK_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Public webhook receiver for Gemini API events
      operationId: v1GeminiWebhook
      x-google-backend:
        address: ${GEMINI_WEBHOOK_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 15.0
      responses:
        "200":
          description: OK

  # --- Superadmin Operations ---

  /v1/admin/moderation:
    options:
      summary: CORS preflight
      operationId: cors_v1adminmoderation
      x-google-backend:
        address: ${ADMIN_LIST_MODERATION_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: List pending moderation queue items (admin/superadmin only)
      operationId: v1AdminListModeration
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_LIST_MODERATION_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/approve:
    options:
      summary: CORS preflight
      operationId: cors_v1adminapprove
      x-google-backend:
        address: ${ADMIN_APPROVE_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Approve a blocked analysis in the moderation queue (admin/superadmin only)
      operationId: v1AdminApproveAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_APPROVE_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/block:
    options:
      summary: CORS preflight
      operationId: cors_v1adminblock
      x-google-backend:
        address: ${ADMIN_BLOCK_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Block a flagged analysis in the moderation queue (admin/superadmin only)
      operationId: v1AdminBlockAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_BLOCK_ANALYSIS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/ban:
    options:
      summary: CORS preflight
      operationId: cors_v1adminban
      x-google-backend:
        address: ${ADMIN_BAN_USER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Ban a user for repeated policy violations (admin/superadmin only)
      operationId: v1AdminBanUser
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_BAN_USER_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/ban-user/{uid}:
    options:
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1adminbanuseruid
      x-google-backend:
        address: ${ADMIN_BAN_USER_PERMANENT_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Permanently ban a user (superadmin only)
      operationId: v1AdminBanUserPermanent
      security:
        - firebase_auth: []
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${ADMIN_BAN_USER_PERMANENT_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/unban-user/{uid}:
    options:
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1adminunbanuseruid
      x-google-backend:
        address: ${ADMIN_UNBAN_USER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Clear ban and restore user access (superadmin only)
      operationId: v1AdminUnbanUser
      security:
        - firebase_auth: []
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${ADMIN_UNBAN_USER_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/reset-quota/{uid}:
    options:
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1adminresetquotauid
      x-google-backend:
        address: ${ADMIN_RESET_QUOTA_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Reset a user's daily quota (superadmin only)
      operationId: v1AdminResetQuota
      security:
        - firebase_auth: []
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${ADMIN_RESET_QUOTA_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/retry-transaction/{txId}:
    options:
      parameters:
        - name: txId
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1adminretrytransactiontxId
      x-google-backend:
        address: ${ADMIN_RETRY_TRANSACTION_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Retry a non-COMPLETED transaction (superadmin only)
      operationId: v1AdminRetryTransaction
      security:
        - firebase_auth: []
      parameters:
        - name: txId
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${ADMIN_RETRY_TRANSACTION_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/retry-failed:
    options:
      summary: CORS preflight
      operationId: cors_v1adminretryfailed
      x-google-backend:
        address: ${ADMIN_RETRY_FAILED_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Bulk retry all FAILED transactions (superadmin only)
      operationId: v1AdminRetryFailed
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_RETRY_FAILED_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /v1/admin/assign-permissions:
    options:
      summary: CORS preflight
      operationId: cors_v1adminassignpermissions
      x-google-backend:
        address: ${ADMIN_ASSIGN_PERMISSIONS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Assign command permissions to a superadmin (two-person rule)
      operationId: v1AdminAssignPermissions
      security:
        - firebase_auth: []
      x-google-backend:
        address: ${ADMIN_ASSIGN_PERMISSIONS_URL}
        path_translation: CONSTANT_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v1/admin/permissions/{uid}:
    options:
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v1adminpermissionsuid
      x-google-backend:
        address: ${ADMIN_GET_PERMISSIONS_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      summary: List a superadmin's assigned command permissions
      operationId: v1AdminGetPermissions
      security:
        - firebase_auth: []
      parameters:
        - name: uid
          in: path
          required: true
          type: string
      x-google-backend:
        address: ${ADMIN_GET_PERMISSIONS_URL}
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 30.0
      responses:
        "200":
          description: OK

  /v0/analyze:
    options:
      summary: CORS preflight
      operationId: cors_v0analyze
      x-google-backend:
        address: "${START_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      operationId: legacyV0Analyze
      security: [ firebase_auth: [] ]
      x-google-backend: { address: "${START_ANALYSIS_URL}", path_translation: CONSTANT_ADDRESS }
      responses: { "200": { description: OK } }

  /v0/analysis/start:
    options:
      summary: CORS preflight
      operationId: cors_v0analysisstart
      x-google-backend:
        address: "${START_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Initiate an Atomic Analysis Transaction (AAT) for B2B.
      operationId: legacyV0AnalysisStart
      security: [ api_key: [] ]
      parameters:
        - name: Idempotency-Key
          in: header
          type: string
          description: A unique UUID v4 to ensure request idempotency and prevent duplicate executions.
          required: false
        - name: body
          in: body
          required: true
          schema:
            type: object
            required:
              - imageBase64
            properties:
              imageBase64: { type: string }
              language: { type: string }
              latitude: { type: number, format: float }
              longitude: { type: number, format: float }
              placement: { type: string, enum: [inside, outside] }
      x-google-backend: { address: "${START_ANALYSIS_URL}", path_translation: CONSTANT_ADDRESS }
      responses: { "200": { description: OK } }

  /v0/analyze-direct:
    options:
      summary: CORS preflight
      operationId: cors_v0analyzedirect
      x-google-backend:
        address: "${GET_PLANT_CARE_ADVICE_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      operationId: legacyV0AnalyzeDirect
      security: [ api_key: [] ]
      parameters:
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              imageBase64: { type: string }
              sunExposure: { type: string }
              wateringFrequency: { type: integer }
              language: { type: string }
              latitude: { type: number, format: float }
              longitude: { type: number, format: float }
              placement: { type: string, enum: [inside, outside] }
      x-google-backend: { address: "${GET_PLANT_CARE_ADVICE_URL}", path_translation: CONSTANT_ADDRESS }
      responses: { "200": { description: OK } }

  /v0/history:
    options:
      summary: CORS preflight
      operationId: cors_v0history
      x-google-backend:
        address: "${GET_ANALYSIS_HISTORY_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      operationId: legacyV0History
      security: [ api_key: [] ]
      x-google-backend: { address: "${GET_ANALYSIS_HISTORY_URL}", path_translation: CONSTANT_ADDRESS }
      responses: { "200": { description: OK } }

  /v0/stats:
    options:
      summary: CORS preflight
      operationId: cors_v0stats
      x-google-backend:
        address: "${GET_STATS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    get:
      operationId: legacyV0Stats
      security: [ api_key: [] ]
      x-google-backend: { address: "${GET_STATS_URL}", path_translation: CONSTANT_ADDRESS }
      responses: { "200": { description: OK } }

  /v0/history/{id}/impression:
    options:
      parameters:
        - name: id
          in: path
          required: true
          type: string
      summary: CORS preflight
      operationId: cors_v0historyidimpression
      x-google-backend:
        address: "${UPDATE_ANALYSIS_IMPRESSION_URL}"
        path_translation: APPEND_PATH_TO_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    put:
      operationId: legacyV0Impression
      security: [ api_key: [] ]
      parameters: 
        - name: id
          in: path
          required: true
          type: string
        - name: body
          in: body
          required: true
          schema:
            type: object
            properties:
              impression:
                type: string
      x-google-backend: { address: "${UPDATE_ANALYSIS_IMPRESSION_URL}", path_translation: APPEND_PATH_TO_ADDRESS }
      responses: { "200": { description: OK } }

  /getPlantCareAdvice:
    options:
      summary: CORS preflight
      operationId: cors_getPlantCareAdvice
      x-google-backend:
        address: "${GET_PLANT_CARE_ADVICE_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy getPlantCareAdvice
      operationId: legacyGetPlantCareAdvice
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${GET_PLANT_CARE_ADVICE_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /startAnalysis:
    options:
      summary: CORS preflight
      operationId: cors_startAnalysis
      x-google-backend:
        address: "${START_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy startAnalysis
      operationId: legacyStartAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${START_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /saveAnalysis:
    options:
      summary: CORS preflight
      operationId: cors_saveAnalysis
      x-google-backend:
        address: "${SAVE_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy saveAnalysis
      operationId: legacySaveAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${SAVE_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /deleteAnalysis:
    options:
      summary: CORS preflight
      operationId: cors_deleteAnalysis
      x-google-backend:
        address: "${DELETE_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy deleteAnalysis
      operationId: legacyDeleteAnalysis
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${DELETE_ANALYSIS_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /updateAnalysisImpression:
    options:
      summary: CORS preflight
      operationId: cors_updateAnalysisImpression
      x-google-backend:
        address: "${UPDATE_ANALYSIS_IMPRESSION_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy updateAnalysisImpression
      operationId: legacyUpdateAnalysisImpression
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${UPDATE_ANALYSIS_IMPRESSION_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /createFolder:
    options:
      summary: CORS preflight
      operationId: cors_createFolder
      x-google-backend:
        address: "${CREATE_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy createFolder
      operationId: legacyCreateFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${CREATE_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /deleteFolder:
    options:
      summary: CORS preflight
      operationId: cors_deleteFolder
      x-google-backend:
        address: "${DELETE_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy deleteFolder
      operationId: legacyDeleteFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${DELETE_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /assignToFolder:
    options:
      summary: CORS preflight
      operationId: cors_assignToFolder
      x-google-backend:
        address: "${ASSIGN_TO_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy assignToFolder
      operationId: legacyAssignToFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${ASSIGN_TO_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK

  /unassignFromFolder:
    options:
      summary: CORS preflight
      operationId: cors_unassignFromFolder
      x-google-backend:
        address: "${UNASSIGN_FROM_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 5.0
      responses:
        "200":
          description: OK
    post:
      summary: Legacy unassignFromFolder
      operationId: legacyUnassignFromFolder
      security:
        - firebase_auth: []
      x-google-backend:
        address: "${UNASSIGN_FROM_FOLDER_URL}"
        path_translation: CONSTANT_ADDRESS
        deadline: 60.0
      responses:
        "200":
          description: OK
