openapi: 3.0.3
info:
  title: Gommo API (documented via Gommo Developer)
  description: |
    **Public Gommo API** — primary integration surface (standard Gommo public API).

    - `https://v2.api.gommo.net` — jobs, models, upload, album library
    - `https://api.gommo.net` — auth, chat, audio, info, library lists

    **Async media jobs (Tier A):** `POST /ai/models` → `POST /ai/jobs/{type}/{modelSlug}` → poll `POST /ai/jobs/{id}` every 3.5s (max ~80). No job-completion webhook on public API.
    Human docs: request lifecycle and read-catalog on Gommo Developer docs site.

    Optional **Gommo Developer** (`http://localhost:3001`) wraps Mode B REST (`/gateway/*`), billing, and admin for local dev.
    See docs/reference/gommo-public-api.md
  version: 0.2.0
  contact:
    url: https://github.com/bruno-hao97/ai-gateway

servers:
  - url: https://v2.api.gommo.net
    description: Gommo V2 — jobs, models, upload, albums
  - url: https://api.gommo.net
    description: Gommo platform — auth, chat, audio, info, library
  - url: http://localhost:3001
    description: Gommo Developer dev (Mode B REST, billing, admin)

tags:
  - name: Gommo V2
    description: Public API on v2.api.gommo.net
  - name: Gommo Platform
    description: Public API on api.gommo.net
  - name: Health
  - name: OpenAI
  - name: Gateway Dev
    description: Gommo Developer Mode B — localhost only
  - name: Billing
  - name: Admin

x-gommo-hosts:
  v2: https://v2.api.gommo.net
  auth: https://api.gommo.net

paths:
  /health:
    get:
      tags: [Health]
      servers:
        - url: http://localhost:3001
      summary: Health check (Gommo Developer)
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /ai/models:
    get:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: List models by job type
      parameters:
        - name: type
          in: query
          required: true
          schema:
            type: string
            example: image
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Model catalog
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: List models (form body)
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                type:
                  type: string
                domain:
                  type: string
                  example: YOUR_REGISTRATION_DOMAIN
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Model catalog

  /ai/jobs/{type}/{modelSlug}:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Create media job (form-urlencoded)
      description: |
        Returns job id (`id_base` in response). Client must poll `POST /ai/jobs/{id}` until result URL or failure — no public webhook.
        Field values (`ratio`, `mode`, …) from `POST /ai/models` only.
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
            example: image
        - name: modelSlug
          in: path
          required: true
          schema:
            type: string
            example: flux-dev
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                domain:
                  type: string
                prompt:
                  type: string
                ratio:
                  type: string
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Job created

  /ai/jobs/{id}:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Poll job status
      description: |
        Repeat every 3.5s, max ~80 attempts. Query `media` must match job type (`image`, `video`, `music`).
        Success when response includes result URL (e.g. imageInfo.result_url).
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: media
          in: query
          required: true
          schema:
            type: string
            enum: [image, video, music]
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Job status

  /ai/upload/image:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Upload image
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload URL

  /ai/upload/video:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Upload video
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                video_file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload URL

  /ai/library/album-images:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Image album list
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                domain:
                  type: string
                project_id:
                  type: string
                  example: default
                limit:
                  type: integer
                  example: 30
                order_by:
                  type: string
                sort_by:
                  type: string
      responses:
        '200':
          description: Album images

  /ai/library/album-videos:
    post:
      tags: [Gommo V2]
      servers:
        - url: https://v2.api.gommo.net
      summary: Video album list
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                domain:
                  type: string
                project_id:
                  type: string
                  example: default
                limit:
                  type: integer
                  example: 30
                order_by:
                  type: string
                sort_by:
                  type: string
      responses:
        '200':
          description: Album videos

  /ai/info/image/{id}:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Image job info
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Image status

  /ai/info/video/{id}:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Video job info
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Video status

  /ai/library/musics:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://v2.api.gommo.net
      summary: Music library list
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Music list

  /ai/library/audios:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://v2.api.gommo.net
      summary: Audio library list
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Audio list

  /api/v2/chat:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Chat or stream
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatRequest'
      responses:
        '200':
          description: JSON or SSE

  /ai/audio:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Platform audio (voices, TTS)
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Audio response

  /ai/me:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Profile and credits
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                access_token:
                  type: string
                domain:
                  type: string
      security:
        - bearerAuth: []
      responses:
        '200':
          description: User profile

  /api/apps/go-mmo/auth/login:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Login (form)
      security: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                email:
                  type: string
                password:
                  type: string
                domain:
                  type: string
      responses:
        '200':
          description: access_token

  /api/apps/go-mmo/auth/change-password:
    post:
      tags: [Gommo Platform]
      servers:
        - url: https://api.gommo.net
      summary: Change account login password (form)
      description: Dealer manage-account screen. Upstream may return HTTP 500 or empty/non-JSON bodies.
      security: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [access_token, domain, current_password, new_password]
              properties:
                access_token:
                  type: string
                domain:
                  type: string
                current_password:
                  type: string
                new_password:
                  type: string
                language:
                  type: string
                  example: EN
      responses:
        '200':
          description: Gommo envelope (pass-through)

  /v1/models:
    get:
      tags: [OpenAI]
      servers:
        - url: http://localhost:3001
      summary: List chat models (OpenAI-compatible)
      description: Returns configured Gommo chat model as an OpenAI-style model list.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Model list
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    example: list
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        object:
                          type: string
                        owned_by:
                          type: string

  /v1/chat/completions:
    post:
      tags: [OpenAI]
      servers:
        - url: http://localhost:3001
      summary: Chat completions (OpenAI-compatible)
      description: |
        OpenAI-compatible chat endpoint mapped to Gommo upstream chat.
        Use `model` as `gpt-5.5::cheap` (model::server) or rely on server defaults.
        Set `stream: true` for SSE chunks in OpenAI format.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messages]
              properties:
                model:
                  type: string
                  example: gpt-5.5::cheap
                messages:
                  type: array
                  items:
                    type: object
                    required: [role, content]
                    properties:
                      role:
                        type: string
                        enum: [system, user, assistant, developer, tool]
                      content:
                        oneOf:
                          - type: string
                          - type: array
                stream:
                  type: boolean
                  default: false
      responses:
        '200':
          description: JSON completion or SSE stream (OpenAI format)

  /gateway/auth/login:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Login with Gommo email and password
      description: |
        Returns a Gommo `access_token` for `Authorization: Bearer` on `/gateway/*`.
        `domain` is optional — server uses `GOMMO_API_DOMAIN` when omitted.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthLoginRequest'
      responses:
        '200':
          description: Login success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '401':
          description: Invalid credentials

  /gateway/auth/register:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Register Gommo user
      description: |
        Creates a Gommo account via merchant credentials on the server
        (`GOMMO_ACCESS_TOKEN`, `GOMMO_MANAGER_ID`). Returns `access_token` on success.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthRegisterRequest'
      responses:
        '200':
          description: Registration success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokenResponse'
        '400':
          description: Validation error
        '503':
          description: Merchant register not configured (GOMMO_ACCESS_TOKEN / GOMMO_MANAGER_ID)
        '502':
          description: Upstream registration error

  /gateway/auth/change-password:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Change signed-in user password
      description: |
        Proxies `POST …/auth/change-password` with Bearer token as `access_token`.
        Valid JSON upstream responses are pass-through. Empty or non-JSON upstream bodies
        return gateway `{ success: false, code: UPSTREAM_ERROR, raw }`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthChangePasswordRequest'
      responses:
        '200':
          description: Upstream Gommo JSON (pass-through)
        '401':
          description: Missing or invalid Bearer
        '502':
          description: Upstream empty or non-JSON body

  /gateway/credits/transfer:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Transfer credits to another user (P2P)
      description: |
        Proxies `POST …/users/sendBalances` with Bearer as `access_token`.
        Default upstream form `type` is `credit_ai`. Valid JSON upstream responses are
        pass-through (including HTTP 200 with `error: 1`). Empty or non-JSON upstream bodies
        return gateway `{ success: false, code: UPSTREAM_ERROR, raw }`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreditsTransferRequest'
      responses:
        '200':
          description: Upstream Gommo JSON (pass-through)
        '400':
          description: Validation error (username, message, value bounds 10k–20M)
        '401':
          description: Missing or invalid Bearer
        '502':
          description: Upstream empty or non-JSON body

  /gateway/credits/transactions:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: List credit transaction history (ledger)
      description: |
        Proxies `POST …/users/paymentsHistory` with Bearer as `access_token`.
        Default `limit` is 500 (dealer UI). Valid JSON upstream responses are pass-through
        (usually `{ data, runtime }`). Empty or non-JSON upstream bodies return gateway
        `{ success: false, code: UPSTREAM_ERROR, raw }`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreditsTransactionsRequest'
      responses:
        '200':
          description: Upstream Gommo JSON (pass-through)
        '400':
          description: Validation error (limit bounds, time range)
        '401':
          description: Missing or invalid Bearer
        '502':
          description: Upstream empty or non-JSON body

  /gateway/models:
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: List models by type (public catalog — Bearer optional)
      description: |
        OpenRouter-style public browse. No Authorization required when upstream allows it;
        gateway may fall back to server merchant token. Create jobs still require Bearer.
      security: []
      parameters:
        - name: type
          in: query
          required: true
          schema:
            type: string
            example: image
        - name: lang
          in: query
          required: false
          description: |
            `en` merges English descriptions from `cache/catalog-descriptions.en.json`.
            Warm cache offline with `npm run catalog:translate` (Gommo chat + `GOMMO_ACCESS_TOKEN`).
            Live translate on cache miss only when `CATALOG_TRANSLATE_ON_REQUEST=true`.
            Omit or `vi` returns upstream Vietnamese text only.
          schema:
            type: string
            enum: [en, vi]
            example: en
      responses:
        '200':
          description: Model catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'

  /gateway/jobs/{segment}:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Create media job (segment = job type)
      security:
        - bearerAuth: []
      parameters:
        - name: segment
          in: path
          required: true
          description: Job type — `image`, `video`, `music`, `tts`, tool types, …
          schema:
            type: string
            example: image
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJobRequest'
      responses:
        '200':
          description: Job created or completed (wait=true)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Poll job status (segment = job id)
      security:
        - bearerAuth: []
      parameters:
        - name: segment
          in: path
          required: true
          description: Job id from create response (`id_base`, `job_id`, …)
          schema:
            type: string
        - name: media
          in: query
          required: true
          schema:
            type: string
            enum: [image, video, music]
      responses:
        '200':
          description: Job status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'

  /gateway/upload/image:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Upload image
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'

  /gateway/upload/video:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Upload video
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                video_file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'

  /gateway/newfeeds:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Home newsfeed (newfeeds)
      description: Proxies `POST …/ai/newfeeds`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                preset:
                  type: string
                  enum: [public_timeline, my_feed, public, mine]
                  description: Bảng tin vs Của tôi
                limit:
                  type: integer
                  example: 30
                projectId:
                  type: string
                  example: default
                afterVideoId:
                  type: string
                afterImageId:
                  type: string
                language:
                  type: string
                  example: VI
                privacy:
                  type: string
                  description: Override preset (PUBLIC or empty string)
      responses:
        '200':
          description: Pass-through newfeeds envelope

  /gateway/explore/public-videos:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Community video explore feed
      description: Proxies platform app `POST …/ai/public-videos`. Bearer optional for public browse.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  example: public_home
                publicPrompt:
                  type: boolean
                  example: false
                language:
                  type: string
                  example: vi
                model:
                  type: string
      responses:
        '200':
          description: Gommo explore envelope (pass-through; data is usually an array of video items)

  /gateway/projects:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: List AI projects (legacy)
      description: Proxies `POST …/ai/projects`. Bearer required.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Pass-through projects list envelope

  /gateway/ai-projects:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: AI projects V2 (list, create, get, update, delete, members, budget, stats, activity)
      description: Proxies `POST …/api/v2/ai-projects`. JSON `action` — `list`, `create`, `get`, `update`, `delete`, `set_password`, `member_list`, `member_kick`, `invite`, `invite_revoke`, `set_budget`, `stats`, `activity_list`. `set_password` needs `id_base`, `password` (empty string clears — dealer tab Chi tiết). `get` optional `password` unlocks `protected` projects; wrong password returns upstream envelope (`needs_password`). `invite` needs `id_base`, `identifier`, `role` (member|mod|admin), `scopes` (object or JSON string); optional `access`, `description`. `invite_revoke` needs `invite_id`. `member_kick` needs `id_base`, `user_id`. `set_budget` needs `id_base`, integer `budget` (0 = unlimited upstream). `stats` needs `id_base`, `period` (all|today|yesterday|this_month|last_month) — project modal Thống kê; not usage-history. `activity_list` needs `id_base`; optional `limit` (default 50), `offset` (default 0) — tab Lịch sử hoạt động audit log.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action:
                  type: string
                  enum: [list, create, get, update, delete, set_password, member_list, member_kick, invite, invite_revoke, set_budget, stats, activity_list]
                  description: Other upstream actions pass through when sent.
                language:
                  type: string
                  example: vi
                name:
                  type: string
                description:
                  type: string
                id_base:
                  type: string
                  description: get, update, delete, set_password, member_list, member_kick, set_budget, stats, activity_list — project id_base from list
                password:
                  type: string
                  description: set_password — required (empty clears). get — optional unlock when project is protected.
                limit:
                  type: integer
                  minimum: 1
                  description: activity_list — page size (dealer UI often 50)
                offset:
                  type: integer
                  minimum: 0
                  description: activity_list — pagination offset (0 first page)
                user_id:
                  type: integer
                  minimum: 1
                  description: member_kick — target user from member_list data.members[].user_id
                userId:
                  type: integer
                  minimum: 1
                  description: Alias for user_id on member_kick
                identifier:
                  type: string
                  description: invite — username or email (upstream resolves user)
                email:
                  type: string
                  description: Alias for identifier on invite
                role:
                  type: string
                  enum: [member, mod, admin]
                access:
                  type: string
                  example: view
                scopes:
                  type: object
                  description: invite — permission tree (JSON string upstream); copy from dealer Network
                invite_id:
                  type: string
                  description: invite_revoke — from member_list data.invites[]
                inviteId:
                  type: string
                budget:
                  type: integer
                  minimum: 0
                  description: set_budget only — project credit cap (0 = unlimited upstream)
                period:
                  type: string
                  enum: [all, today, yesterday, this_month, last_month]
                  description: stats only — dealer tab Thống kê time filter
                statsPeriod:
                  type: string
                  description: Alias for period on stats
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through V2 projects envelope

  /gateway/files-manager:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Project media files (images / videos)
      description: Proxies `POST …/api/v2/files-manager` with typed `action=list`. Project modal Media tab — field **`project`** (UUID), **`media_type`** image|video, optional `status` (ready), `signed_previews`, `limit`, `offset`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [list]
                  default: list
                project:
                  type: string
                  description: Project UUID — alias project_id, id_base
                project_id:
                  type: string
                id_base:
                  type: string
                media_type:
                  type: string
                  enum: [image, video]
                mediaType:
                  type: string
                status:
                  type: string
                  example: ready
                signed_previews:
                  type: boolean
                signedPreviews:
                  type: boolean
                limit:
                  type: integer
                  minimum: 1
                offset:
                  type: integer
                  minimum: 0
                language:
                  type: string
      responses:
        '200':
          description: Pass-through files-manager envelope

  /gateway/project-media/audio:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Project media — audio list
      description: Proxies `POST …/ai/audio` with **`action_type=get.list`** and **`project_id`** (dealer Media → Audio tab). Not TTS create/searchVoices.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                project_id:
                  type: string
                project:
                  type: string
                id_base:
                  type: string
                language:
                  type: string
      responses:
        '200':
          description: Pass-through audio envelope

  /gateway/project-media/music:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Project media — music getAll
      description: Proxies `POST …/api/apps/go-mmo/ai_musics/getAll` with `project_id` (dealer Media → Music tab).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                project_id:
                  type: string
                project:
                  type: string
                id_base:
                  type: string
                language:
                  type: string
      responses:
        '200':
          description: Pass-through music list envelope

  /gateway/credit-pools:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Credit pools V2 (pass-through)
      description: Proxies `POST …/api/v2/credit-pools`. Verified `create`, `deposit` (Bơm — pool_id, amount), `delete`, `ledger`, `list`, `set_share_credit`. `deposit` — wallet must keep 100k reserve (need amount+100k); may return error:1 in JSON. `delete` — ok:true does not imply wallet refund. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action:
                  type: string
                  enum: [list, ledger, create, deposit, delete, set_share_credit]
                  description: Verified actions; others pass through
                  example: list
                scope_type:
                  type: string
                  example: project
                scope_id:
                  type: string
                  description: Project id_base when scope_type is project
                id_base:
                  type: string
                  description: Project id for set_share_credit (aliases scope_id / projectId)
                scopeType:
                  type: string
                scopeId:
                  type: string
                currency:
                  type: string
                  example: credits_ai
                  description: Legacy alias; create uses currency_name
                currency_name:
                  type: string
                  example: credit_ai
                name:
                  type: string
                  description: create — pool name
                status:
                  type: string
                  example: shared
                tags:
                  type: string
                note:
                  type: string
                  description: create or deposit — optional memo
                amount:
                  type: integer
                  description: create or deposit — positive integer credits
                enabled:
                  type: integer
                  enum: [0, 1]
                  description: set_share_credit request toggle
                share_credit_enabled:
                  type: integer
                  enum: [0, 1]
                  description: Legacy alias for enabled on set_share_credit
                limit:
                  type: integer
                page:
                  type: integer
                pool_id:
                  type: string
                  description: deposit or delete — pool UUID from list data.pools[].id
                poolId:
                  type: string
                  description: Gateway alias for pool_id
                language:
                  type: string
                  example: vi
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through credit pools envelope

  /gateway/devices:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Session devices
      description: Proxies `POST …/api/v2/devices` (manage-account Devices). Bearer required. JSON `action` register | list | rename | disable.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [register, list, rename, disable]
                  default: list
                current_device_id:
                  type: string
                  description: Browser session UUID for list/register
                language:
                  type: string
                  example: EN
                device_id:
                  type: string
                  description: Target UUID for rename/disable (not id_base)
                target_device_id:
                  type: string
                  description: Gateway alias for device_id
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through devices envelope `{ success, message?, data }`

  /gateway/support/tickets:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Support — list tickets
      description: Proxies `POST …/api/apps/go-mmo/support/myTickets`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                language:
                  type: string
                  example: EN
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through `{ data[], runtime? }`

  /gateway/support/tickets/create:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Support — create ticket
      description: Proxies `POST …/api/apps/go-mmo/support/create`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [subject, description]
              properties:
                subject:
                  type: string
                issue_type:
                  type: string
                  example: bug
                description:
                  type: string
                language:
                  type: string
      responses:
        '200':
          description: Pass-through `{ ticket_id_hash, runtime? }`

  /gateway/support/tickets/detail:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Support — ticket detail
      description: Proxies `POST …/api/apps/go-mmo/support/detail`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [ticket_id_base]
              properties:
                ticket_id_base:
                  type: string
                  example: SUP-20260925-7022
                language:
                  type: string
      responses:
        '200':
          description: Pass-through `{ ticket, messages[], attachments[], runtime? }`

  /gateway/support/tickets/reply:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Support — reply on ticket
      description: Proxies `POST …/api/apps/go-mmo/support/reply`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [ticket_id_base, content]
              properties:
                ticket_id_base:
                  type: string
                content:
                  type: string
                language:
                  type: string
      responses:
        '200':
          description: Pass-through `{ success, runtime? }`

  /gateway/partner-program:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Partner program — status or apply
      description: Proxies `POST …/api/apps/go-mmo/users/partner_program`. Bearer required. `action_type` status | apply.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [action_type]
              properties:
                action_type:
                  type: string
                  enum: [status, apply]
                  example: status
                lang:
                  type: string
                  example: en
                language:
                  type: string
                  example: EN
                bank_name:
                  type: string
                account_name:
                  type: string
                account_number:
                  type: string
                bank_extra:
                  type: string
                contact:
                  type: string
                email:
                  type: string
                reason:
                  type: string
                accept_rules:
                  oneOf:
                    - type: integer
                    - type: string
                  example: 1
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through `{ success, program?, application?, message?, runtime? }`

  /gateway/creator-rewards:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Creator Rewards — status, pool, or apply
      description: Proxies `POST …/api/apps/go-mmo/users/creator_rewards`. Bearer required. `action_type` status | pool | apply.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [action_type]
              properties:
                action_type:
                  type: string
                  enum: [status, pool, apply]
                  example: status
                language:
                  type: string
                  example: EN
                payout_pref:
                  type: string
                  enum: [credit, cash]
                  example: credit
                contact:
                  type: string
                email:
                  type: string
                reason:
                  type: string
                bank_name:
                  type: string
                account_name:
                  type: string
                account_number:
                  type: string
                bank_extra:
                  type: string
                accept_rules:
                  oneOf:
                    - type: integer
                    - type: string
                  example: 1
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Pass-through `{ success, program?, pool?, message?, runtime? }`

  /gateway/changelogs:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Changelogs — recent updates / release notes
      description: Proxies `POST …/api/apps/go-mmo/app/changelogs`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                limit:
                  type: integer
                  example: 12
                app:
                  type: string
                  example: ai
                format:
                  type: string
                  example: flat
                language:
                  type: string
                  example: EN
                lang_id:
                  type: string
                  example: en
                  description: Alias for language when copying from News bodies (upstream still receives `language`).
                langId:
                  type: string
                  description: Alias for language.
                site_domain:
                  type: string
                siteDomain:
                  type: string
                  description: Alias for site_domain.
      responses:
        '200':
          description: Pass-through `{ data[], runtime? }`

  /gateway/news:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: News & updates (dealer CMS)
      description: Proxies `POST …/api/apps/go-mmo/news/getAll`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                limit:
                  type: integer
                  example: 100
                lang_id:
                  type: string
                  example: en
                language:
                  type: string
                  description: Alias mapped to lang_id.
      responses:
        '200':
          description: Pass-through news list `{ data[], runtime? }`

  /gateway/notifications:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Notifications inbox
      description: Proxies `POST …/ai/notifications`. Bearer required.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                limit:
                  type: integer
                page:
                  type: integer
      responses:
        '200':
          description: Pass-through notifications envelope

  /gateway/info/image/{id}:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Image job info
      description: Proxies `POST …/ai/info/image/{id}`. Bearer required.
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Pass-through image info envelope

  /gateway/info/video/{id}:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Video job info
      description: Proxies `POST …/ai/info/video/{id}`. Bearer required.
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Pass-through video info envelope

  /gateway/info/music/{id}:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Music job info
      description: Proxies `POST …/ai/info/music/{id}`. Bearer required.
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Pass-through music info envelope

  /gateway/explore/images:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Community image explore feed
      description: Proxies platform app `POST …/ai/images`. Bearer optional for public browse.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                projectId:
                  type: string
                  example: default
                afterId:
                  type: string
                  description: Pagination cursor (id_base of last item)
                feedType:
                  type: string
                  description: Upstream form field feed_type (alias sort for older clients)
                  example: suggested
                search:
                  type: string
                category:
                  type: string
                model:
                  type: string
                language:
                  type: string
      responses:
        '200':
          description: Gommo explore envelope (pass-through)

  /gateway/library/{resource}:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Library list or album (v2 upstream)
      security:
        - bearerAuth: []
      parameters:
        - name: resource
          in: path
          required: true
          schema:
            type: string
            enum:
              - album-images
              - album-videos
              - musics
              - audios
              - images
              - videos
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                projectId:
                  type: string
                  example: default
                limit:
                  type: integer
                  example: 30
                order_by:
                  type: string
                sort_by:
                  type: string
      responses:
        '200':
          description: Gommo library envelope (same shape as upstream v2)

  /gateway/chat:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Chat agent or stream
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatRequest'
      responses:
        '200':
          description: JSON or SSE stream (client must parse SSE chunks when Content-Type is text/event-stream)

  /gateway/chat-models:
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Chat model catalog (merged upstream models)
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Model list for chat UI

  /gateway/chat-sessions:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Chat session sync (server storage)
      description: Proxies `POST …/api/v2/ai-chat-sessions`. JSON uses camelCase; maps to upstream form fields.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatSessionRequest'
      responses:
        '200':
          description: Pass-through upstream JSON

  /gateway/audio/voices:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Search voices
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                server:
                  type: string
                page:
                  type: integer
      responses:
        '200':
          description: Voice list

  /gateway/audio/tts:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Text-to-speech
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [text, voice_id, server]
              properties:
                text:
                  type: string
                voice_id:
                  type: string
                server:
                  type: string
                model:
                  type: string
      responses:
        '200':
          description: TTS file URL

  /gateway/audio/lists:
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Audio history
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Audio lists

  /gateway/usage/stats:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage statistics (Gommo, preferred)
      description: |
        Aggregated usage from Gommo `POST /api/v2/usage-history` with `action=stats`.
        Send `device_id`, `device_name`, `device_info` in form body (like Gommo platform apps).
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                period:
                  type: string
                  enum: [7d, 30d, 90d]
                type:
                  type: string
                  enum: [all, image, video, audio, music]
                language:
                  type: string
                  default: vi
                domain:
                  type: string
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Usage stats (summary, chart, table)
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage statistics (standard Gommo public API)
      description: |
        Aggregated usage from Gommo `POST /api/v2/usage-history` with `action=stats`.
        Returns summary, chart series, and daily table — same data as Gommo upstream usage history.
      security:
        - bearerAuth: []
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [7d, 30d, 90d]
            default: 30d
        - name: type
          in: query
          schema:
            type: string
            enum: [all, image, video, audio, music]
            default: all
        - name: language
          in: query
          schema:
            type: string
            default: vi
        - name: device_id
          in: query
          description: Browser device id (forwarded to Gommo usage-history)
          schema:
            type: string
        - name: device_name
          in: query
          schema:
            type: string
        - name: device_info
          in: query
          schema:
            type: string
        - name: domain
          in: query
          schema:
            type: string
        - name: projectId
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Usage stats (summary, chart, table)

  /gateway/usage/logs:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage job logs (Gommo items table)
      description: |
        Per-job rows from Gommo `POST /api/v2/usage-history` with `action=logs`.
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                period:
                  type: string
                  enum: [7d, 30d, 90d]
                type:
                  type: string
                language:
                  type: string
                  default: vi
                limit:
                  type: integer
                  default: 30
                page:
                  type: integer
                domain:
                  type: string
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Usage log items
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage job logs (query params)
      security:
        - bearerAuth: []
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [7d, 30d, 90d]
        - name: type
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 30
        - name: page
          in: query
          schema:
            type: integer
        - name: domain
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Usage log items

  /gateway/usage/list:
    post:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage job list (deprecated — use /usage/logs)
      deprecated: true
      description: |
        Alias for `/gateway/usage/logs`. Gommo uses `action=logs`, not `list`.
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                period:
                  type: string
                  enum: [7d, 30d, 90d]
                type:
                  type: string
                language:
                  type: string
                page:
                  type: integer
                limit:
                  type: integer
                domain:
                  type: string
                device_id:
                  type: string
                device_name:
                  type: string
                device_info:
                  type: string
      responses:
        '200':
          description: Usage list items
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage job list (query params)
      security:
        - bearerAuth: []
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [7d, 30d, 90d]
        - name: type
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
        - name: limit
          in: query
          schema:
            type: integer
        - name: domain
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Usage list items

  /gateway/usage/history:
    get:
      tags: [Gateway Dev]
      servers:
        - url: http://localhost:3001
      summary: Usage history (deprecated — use /usage/logs)
      deprecated: true
      description: Alias for `/gateway/usage/logs` (Gommo usage-history action=logs).
      security:
        - bearerAuth: []
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum: [all, image, video]
            default: all
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: projectId
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Usage records

  /billing/status:
    get:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Billing configuration status
      description: |
        Returns Gommo payment availability (`gommoPayment`, `billingMode`),
        legacy PayOS flags, and merchant readiness.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Status flags
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BillingStatusResponse'

  /billing/packages:
    get:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Credit packages
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Package list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreditPackagesResponse'

  /billing/payment/create:
    post:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Create Gommo credit payment (default top-up)
      description: |
        Proxies Gommo `subscriptions/create_payment`. Persists a local order with
        `source: gommo`. Returns QR image, bank transfer details, and VAT breakdown.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GommoPaymentCreateRequest'
      responses:
        '200':
          description: Payment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GommoPaymentCreateResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Missing or invalid Bearer
        '403':
          description: Username does not match token
        '502':
          description: Upstream Gommo error

  /billing/payment/sync:
    post:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Sync Gommo payment status
      description: |
        Proxies Gommo `subscriptions/payment_sync`. When `paid` is true, marks the
        local top-up order as credited.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GommoPaymentSyncRequest'
      responses:
        '200':
          description: Sync result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GommoPaymentSyncResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Missing or invalid Bearer
        '502':
          description: Upstream Gommo error

  /billing/topup/create:
    post:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Create PayOS topup order (legacy)
      deprecated: true
      description: Legacy PayOS checkout. Prefer `/billing/payment/create` (Gommo top-up).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, packageId]
              properties:
                username:
                  type: string
                packageId:
                  type: string
      responses:
        '200':
          description: Checkout URL and orderCode

  /billing/topup/orders:
    get:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: List topup orders for signed-in user
      security:
        - bearerAuth: []
      parameters:
        - name: username
          in: query
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: Order list (newest first)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TopupOrder'
        '401':
          description: Missing or invalid Bearer
        '403':
          description: Username does not match token

  /billing/topup/orders/{orderCode}:
    get:
      tags: [Billing]
      servers:
        - url: http://localhost:3001
      summary: Get order status
      security:
        - bearerAuth: []
      parameters:
        - name: orderCode
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Order details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/TopupOrder'

  /admin/merchant/balance:
    get:
      tags: [Admin]
      servers:
        - url: http://localhost:3001
      summary: Merchant balance
      security:
        - adminKey: []
      responses:
        '200':
          description: Balance

  /admin/credits/send:
    post:
      tags: [Admin]
      servers:
        - url: http://localhost:3001
      summary: Send credits to user
      security:
        - adminKey: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [username, value]
              properties:
                username:
                  type: string
                value:
                  type: number
                message:
                  type: string
                type:
                  type: string
      responses:
        '200':
          description: Credits sent

  /admin/users/register:
    post:
      tags: [Admin]
      servers:
        - url: http://localhost:3001
      summary: Register Gommo user (merchant)
      security:
        - adminKey: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                password:
                  type: string
                phone:
                  type: string
      responses:
        '200':
          description: User registered

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Gommo access_token
    adminKey:
      type: apiKey
      in: header
      name: x-admin-key

  schemas:
    AuthLoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        domain:
          type: string
          description: Optional Gommo domain; server default when omitted

    AuthRegisterRequest:
      type: object
      required: [email, password, phone]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        phone:
          type: string
        name:
          type: string
        note:
          type: string

    AuthChangePasswordRequest:
      type: object
      required: [current_password, new_password]
      properties:
        current_password:
          type: string
          format: password
        new_password:
          type: string
          format: password
        language:
          type: string
          description: Upstream form field (e.g. EN, VI)
        device_id:
          type: string
        device_name:
          type: string
        device_info:
          type: string

    CreditsTransferRequest:
      type: object
      required: [username, value, message]
      properties:
        username:
          type: string
          description: Recipient Gommo username
        value:
          type: integer
          minimum: 10000
          maximum: 20000000
          description: Credits to send
        message:
          type: string
          description: Required transfer note
        type:
          type: string
          default: credit_ai
          description: Upstream form type (dealer UI uses credit_ai)
        language:
          type: string
          description: Upstream locale (EN, VI, …)
        device_id:
          type: string
        device_name:
          type: string
        device_info:
          type: string

    CreditsTransactionsRequest:
      type: object
      properties:
        limit:
          type: integer
          minimum: 1
          maximum: 500
          default: 500
          description: Rows per request (dealer UI uses 500)
        currency_name:
          type: string
          description: Filter currency; empty string = all types
        source:
          type: string
          description: Filter ledger source; empty = all
        time_start:
          type: integer
          minimum: 0
          default: 0
          description: Unix timestamp lower bound; 0 = none
        time_end:
          type: integer
          minimum: 0
          default: 0
          description: Unix timestamp upper bound; 0 = none
        language:
          type: string
          description: Upstream locale (EN, VI, …)
        device_id:
          type: string
        device_name:
          type: string
        device_info:
          type: string

    AuthTokenResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            access_token:
              type: string
        message:
          type: string

    HealthResponse:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            ok:
              type: boolean
            merchantConfigured:
              type: boolean
            adminConfigured:
              type: boolean

    SuccessEnvelope:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
        message:
          type: string
        code:
          type: string

    ErrorEnvelope:
      type: object
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
        code:
          type: string
          enum:
            - UNAUTHORIZED
            - FORBIDDEN
            - VALIDATION_ERROR
            - NOT_CONFIGURED
            - UPSTREAM_ERROR
            - RATE_LIMITED
            - INSUFFICIENT_CREDITS
            - INTERNAL_ERROR

    CreateJobRequest:
      type: object
      required: [modelSlug, fields]
      properties:
        modelSlug:
          type: string
        wait:
          type: boolean
          default: false
        fields:
          type: object
          additionalProperties: true

    ChatRequest:
      type: object
      required: [action, messages]
      properties:
        action:
          type: string
          enum: [chat, stream, set_model]
        query:
          type: string
        sessionId:
          type: string
        language:
          type: string
          example: EN
          description: Upstream form language; gateway default VI when omitted.
        model:
          type: string
          description: Chat model id (stream / set_model).
        sessionId:
          type: string
          description: Session UUID; upstream chat_id / session_id.
        messages:
          type: array
          items:
            type: object
            properties:
              role:
                type: string
              text:
                type: string

    ChatSessionRequest:
      type: object
      required: [action]
      properties:
        action:
          type: string
          enum: [save_message, list_sessions, get_messages, get_session]
        sessionId:
          type: string
        messageId:
          type: string
        role:
          type: string
          enum: [user, model]
        text:
          type: string
        limit:
          type: integer
        offset:
          type: integer
        cursor:
          type: string

    CreditPackage:
      type: object
      required: [id, gommoIdBase, name, amountVnd, credits, bonusPercent]
      properties:
        id:
          type: string
          example: basic-member
        gommoIdBase:
          type: string
          description: Gommo `id_base` for create_payment
          example: credit-basic
        name:
          type: string
          example: BASIC - MEMBER
        amountVnd:
          type: integer
          description: Pre-VAT plan price (Gommo platform priceVND on /prices)
        credits:
          type: integer
          example: 50000
        bonusPercent:
          type: integer
          example: 0
        featured:
          type: boolean
          example: false

    CreditPackagesResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/CreditPackage'

    BillingStatusResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            gommoPayment:
              type: boolean
              example: true
            billingMode:
              type: string
              enum: [gommo, legacy]
              example: gommo
            payosConfigured:
              type: boolean
            merchantReady:
              type: boolean
            webhookUrl:
              type: string
              nullable: true
            returnUrl:
              type: string
              nullable: true

    InvoiceBuyer:
      type: object
      required: [type, name]
      properties:
        type:
          type: string
          enum: [consumer, personal, company]
        name:
          type: string
        email:
          type: string
        phone:
          type: string
        address:
          type: string
        national_id:
          type: string
        tax_code:
          type: string
        gui_name:
          type: string
        referral_code:
          type: string

    GommoPaymentCreateRequest:
      type: object
      required: [username, packageId]
      properties:
        username:
          type: string
          description: Gommo username — must match Bearer token identity
        packageId:
          type: string
          example: ultra-member
        invoiceBuyer:
          $ref: '#/components/schemas/InvoiceBuyer'
        promoCode:
          type: string
        referralCode:
          type: string
        device_id:
          type: string
        device_name:
          type: string
        device_info:
          type: string
          description: JSON string forwarded to Gommo

    GommoPaymentResult:
      type: object
      properties:
        url:
          type: string
        orderCode:
          type: string
          example: SP1234567890
        content:
          type: string
          description: Bank transfer memo
        amountVnd:
          type: integer
        amountBaseVnd:
          type: integer
        vatAmountVnd:
          type: integer
        vatPercent:
          type: integer
          example: 5
        qrImage:
          type: string
        qrFallback:
          type: string
        gateway:
          type: string
        paymentServer:
          type: string
        bank:
          type: string
        acc:
          type: string
        holder:
          type: string
        store:
          type: string
        packageId:
          type: string
        credits:
          type: integer

    GommoPaymentCreateResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          $ref: '#/components/schemas/GommoPaymentResult'

    GommoPaymentSyncRequest:
      type: object
      required: [orderCode]
      properties:
        orderCode:
          type: string
        device_id:
          type: string
        device_name:
          type: string
        device_info:
          type: string

    GommoPaymentSyncResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            paid:
              type: boolean
            orderCode:
              type: string
            deposit:
              type: object
              additionalProperties: true
            sync:
              type: object
              additionalProperties: true

    TopupOrder:
      type: object
      properties:
        orderCode:
          type: string
        username:
          type: string
        packageId:
          type: string
        amountVnd:
          type: integer
        credits:
          type: integer
        status:
          type: string
          enum: [pending, paid, credited, failed]
        source:
          type: string
          enum: [gommo, payos]
        createdAt:
          type: string
          format: date-time
        paidAt:
          type: string
          format: date-time
        creditedAt:
          type: string
          format: date-time
