openapi: 3.1.0
info:
  title: Koneth Management API
  version: 1.0.0
  description: |
    Use the Koneth Management API to manage trading accounts, account funds,
    server configuration, metrics, commands, and API credits from your backend.

    Keep management keys on a trusted server. Do not call this API directly
    from browser or mobile applications.
  contact:
    name: Koneth
    url: https://github.com/koneth-trader
servers:
  - url: https://api.koneth.com
    description: Production
  - url: http://127.0.0.1:8788
    description: Local development
security:
  - managementKey: []
tags:
  - name: Credits
    description: Inspect your organization's API credit wallet.
  - name: Server configuration
    description: Read and update trading server configuration.
  - name: Accounts
    description: Create, inspect, and manage trading accounts.
  - name: Finance
    description: Deposit or withdraw trading account funds.
  - name: Commands
    description: Track asynchronous account and finance operations.
  - name: Metrics
    description: Read account risk and performance snapshots.
paths:
  /api/v1/management/credits/balance:
    get:
      operationId: getCreditBalance
      summary: Get API credit balance
      description: |
        Returns the organization's API credit wallet and low-balance state.
        Requires `credits:read`.
      tags: [Credits]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Credit wallet returned.
          content:
            application/json:
              schema:
                type: object
                required: [wallet]
                properties:
                  wallet:
                    $ref: "#/components/schemas/CreditWallet"
              example:
                wallet:
                  id: 0f6f2d88-9357-4ce6-9b9e-76db2f98bb2e
                  organizationId: b7378db4-5a7a-4e69-a8df-04f2e6d94243
                  availableCredits: 24890
                  reservedCredits: 0
                  lifetimePurchased: 30000
                  lifetimeConsumed: 5110
                  lowBalanceThreshold: 10000
                  createdAt: "2026-08-01T09:00:00.000Z"
                  updatedAt: "2026-08-25T01:30:00.000Z"
                  lowBalance: false
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/configuration:
    parameters:
      - $ref: "#/components/parameters/ServerId"
    get:
      operationId: getServerConfiguration
      summary: Get server configuration
      description: Returns the target trading server's configuration. Requires `accounts:read`.
      tags: [Server configuration]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Server configuration returned.
          content:
            application/json:
              schema:
                type: object
                required: [server]
                properties:
                  server:
                    $ref: "#/components/schemas/ServerConfiguration"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }
    patch:
      operationId: updateServerConfiguration
      summary: Update server configuration
      description: |
        Replaces the supported leverage list and currency for the target server.
        Requires `accounts:update`.
      tags: [Server configuration]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateServerConfigurationRequest"
            example:
              supportedLeverage: [50, 100, 200, 500]
              defaultLeverage: 100
              currency: USD
      responses:
        "200":
          description: Server configuration updated.
          content:
            application/json:
              schema:
                type: object
                required: [server]
                properties:
                  server:
                    $ref: "#/components/schemas/ServerConfiguration"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts:
    parameters:
      - $ref: "#/components/parameters/ServerId"
    get:
      operationId: listTradingAccounts
      summary: List trading accounts
      description: Returns trading accounts managed on a server. Requires `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Accounts returned.
          content:
            application/json:
              schema:
                type: object
                required: [accounts]
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: "#/components/schemas/TradingAccount"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }
    post:
      operationId: createTradingAccount
      summary: Create a trading account
      description: |
        Queues a trading account creation command. Requires `accounts:create`.
        The first accepted request returns one-time trading and investor passwords.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAccountRequest"
            example:
              accountName: Client 1042 Evaluation
              initialBalance: 100000
              leverage: 100
              requestId: onboarding-client-1042
      responses:
        "200":
          description: An earlier command was returned for the idempotency key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateAccountResponse"
        "202":
          description: Account creation queued. Store the returned credentials securely.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateAccountResponse"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/bulk:
    post:
      operationId: bulkCreateTradingAccounts
      summary: Bulk-create trading accounts
      description: |
        Queues between 1 and 100 account creation commands. Koneth appends each
        account's array index to the supplied idempotency key. Requires `accounts:create`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [accounts]
              properties:
                accounts:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    $ref: "#/components/schemas/CreateAccountRequest"
      responses:
        "202":
          description: Account creation commands queued.
          content:
            application/json:
              schema:
                type: object
                required: [results]
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/CreateAccountResponse"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}:
    parameters:
      - $ref: "#/components/parameters/ServerId"
      - $ref: "#/components/parameters/AccountNumber"
    get:
      operationId: getTradingAccount
      summary: Get a trading account
      description: |
        Returns account details and credentials. Requires `accounts:read`.
        Transactions, orders, positions, and deals use their separate paginated
        endpoints so this response remains bounded. Protect the credentials object
        and do not forward this response directly to a webhook or client application.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Account details returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountDetailsResponse"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }
    patch:
      operationId: updateTradingAccount
      summary: Update a trading account
      description: Queues an account name or leverage update. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAccountRequest"
            example:
              accountName: Client 1042 Funded
              leverage: 200
              requestId: promote-client-1042
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }
    delete:
      operationId: deleteTradingAccountImmediately
      summary: Delete a trading account immediately
      description: |
        Deletes the account from the target server and removes its management
        projection and metric snapshots. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Account deleted.
          content:
            application/json:
              schema:
                type: object
                required: [accountNumber, deleted]
                properties:
                  accountNumber:
                    type: string
                  deleted:
                    type: boolean
                    const: true
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/transactions:
    parameters:
      - $ref: "#/components/parameters/ServerId"
      - $ref: "#/components/parameters/AccountNumber"
    get:
      operationId: listTradingAccountTransactions
      summary: List account transactions
      description: |
        Returns a newest-first page of ledger transactions for one trading account.
        Deposit and withdrawal records include the date and time they were recorded.
        Requires `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageLimit"
      responses:
        "200":
          description: Transaction page returned.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TransactionPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/orders:
    parameters:
      - $ref: "#/components/parameters/ServerId"
      - $ref: "#/components/parameters/AccountNumber"
    get:
      operationId: listTradingAccountOrders
      summary: List account orders
      description: |
        Returns a newest-first page of orders for one trading account, including
        creation and execution timestamps when available. Requires `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageLimit"
      responses:
        "200":
          description: Order page returned.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/positions:
    parameters:
      - $ref: "#/components/parameters/ServerId"
      - $ref: "#/components/parameters/AccountNumber"
    get:
      operationId: listTradingAccountPositions
      summary: List account positions
      description: |
        Returns a newest-first page of open and closed positions for one trading
        account. Each record includes its opening time and final closing time when
        fully closed. Use `status=open&limit=1` to confirm whether any open position
        remains before ending a live-metrics subscription. Requires `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageLimit"
        - name: status
          in: query
          description: Filter positions by lifecycle status.
          schema:
            type: string
            enum: [all, open, closed]
            default: all
      responses:
        "200":
          description: Position page returned.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PositionPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/deals:
    parameters:
      - $ref: "#/components/parameters/ServerId"
      - $ref: "#/components/parameters/AccountNumber"
    get:
      operationId: listTradingAccountDeals
      summary: List account deals
      description: |
        Returns a newest-first page of executions for one trading account. Use deals
        to inspect each entry, exit, and partial-close timestamp. Requires
        `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageLimit"
      responses:
        "200":
          description: Deal page returned.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DealPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/enable:
    post:
      operationId: enableTradingAccount
      summary: Enable a trading account
      description: Queues an account enable command. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/AccountAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/disable:
    post:
      operationId: disableTradingAccount
      summary: Disable a trading account
      description: Queues an account disable command. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/AccountAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/archive:
    post:
      operationId: archiveTradingAccount
      summary: Archive a trading account
      description: Queues an account archive command. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/AccountAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/delete:
    post:
      operationId: queueTradingAccountDeletion
      summary: Queue trading account deletion
      description: Queues an account deletion command. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/AccountAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/password-reset:
    post:
      operationId: resetTradingPassword
      summary: Reset the trading password
      description: |
        Queues a trading password reset. Omit `password` to have Koneth generate
        one. The response returns the new password once. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/PasswordReset"
      responses:
        "200": { $ref: "#/components/responses/PasswordCommand" }
        "202": { $ref: "#/components/responses/PasswordCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/investor-password-reset:
    post:
      operationId: resetInvestorPassword
      summary: Reset the investor password
      description: |
        Queues an investor password reset. Omit `password` to have Koneth generate
        one. The response returns the new password once. Requires `accounts:update`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/PasswordReset"
      responses:
        "200": { $ref: "#/components/responses/PasswordCommand" }
        "202": { $ref: "#/components/responses/PasswordCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/exports/history:
    post:
      operationId: exportTradingAccountHistory
      summary: Export account history
      description: Queues an account history export. Requires `accounts:read`.
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HistoryExportRequest"
            example:
              from: "2026-08-01T00:00:00.000Z"
              to: "2026-08-25T00:00:00.000Z"
              limit: 1000
              requestId: history-client-1042-august
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/finance/deposits:
    post:
      operationId: depositTradingAccountFunds
      summary: Deposit account funds
      description: Queues a deposit greater than 0 and no more than 1,000,000,000. Requires `finance:deposit`.
      tags: [Finance]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/FinanceAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/accounts/{accountNumber}/finance/withdrawals:
    post:
      operationId: withdrawTradingAccountFunds
      summary: Withdraw account funds
      description: Queues a withdrawal greater than 0 and no more than 1,000,000,000. Requires `finance:withdraw`.
      tags: [Finance]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/AccountNumber"
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        $ref: "#/components/requestBodies/FinanceAction"
      responses:
        "200": { $ref: "#/components/responses/DuplicateCommand" }
        "202": { $ref: "#/components/responses/AcceptedCommand" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/commands/{commandId}:
    get:
      operationId: getCommand
      summary: Get a command
      description: Returns an asynchronous command by ID. Requires `accounts:read`.
      tags: [Commands]
      parameters:
        - $ref: "#/components/parameters/CommandId"
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Command returned.
          content:
            application/json:
              schema:
                type: object
                required: [command]
                properties:
                  command:
                    $ref: "#/components/schemas/Command"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/commands:
    get:
      operationId: listServerCommands
      summary: List server commands
      description: Returns recent asynchronous commands for a server. Requires `accounts:read`.
      tags: [Commands]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/RequestId"
        - name: limit
          in: query
          description: Maximum number of commands to return.
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 100
      responses:
        "200":
          description: Commands returned in descending creation order.
          content:
            application/json:
              schema:
                type: object
                required: [commands]
                properties:
                  commands:
                    type: array
                    items:
                      $ref: "#/components/schemas/Command"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }

  /api/v1/management/servers/{serverId}/account-metrics:
    get:
      operationId: listAccountMetrics
      summary: List account metrics
      description: |
        Returns available account metric snapshots for a server. Requires `metrics:read`.
        This is not a live valuation request and does not include trade or transaction
        details. Snapshots can be unavailable or delayed. An empty response does not
        indicate zero risk. Results are bounded and do not include a replay cursor.
        To request just the latest stored snapshot for one account, set
        `accountNumber` and `limit=1`. This does not request a fresh valuation.
      tags: [Metrics]
      parameters:
        - $ref: "#/components/parameters/ServerId"
        - $ref: "#/components/parameters/RequestId"
        - name: accountNumber
          in: query
          description: Filter snapshots to one trading account.
          schema:
            type: string
            minLength: 1
            maxLength: 32
        - name: from
          in: query
          description: Return snapshots measured at or after this timestamp.
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of snapshots to return.
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
      responses:
        "200":
          description: Metric snapshots returned in descending measurement order.
          content:
            application/json:
              schema:
                type: object
                required: [snapshots]
                properties:
                  snapshots:
                    type: array
                    items:
                      $ref: "#/components/schemas/AccountMetricSnapshot"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/InsufficientCredits" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "500": { $ref: "#/components/responses/InternalError" }

components:
  securitySchemes:
    managementKey:
      type: apiKey
      in: header
      name: Authorization
      description: Enter `Management kapi.<key-id>.<secret>`.
  parameters:
    ServerId:
      name: serverId
      in: path
      required: true
      description: UUID of a trading server owned by your organization.
      schema:
        type: string
        format: uuid
      example: 6be9a699-2f28-402e-b64e-93ba6bdafe1b
    AccountNumber:
      name: accountNumber
      in: path
      required: true
      description: Trading account number.
      schema:
        type: string
        minLength: 4
        maxLength: 32
        pattern: "^[A-Za-z0-9_-]+$"
      example: "7001042"
    CommandId:
      name: commandId
      in: path
      required: true
      description: UUID returned by an asynchronous mutation.
      schema:
        type: string
        format: uuid
      example: 70d55344-8408-4ccb-b565-0e975cc0168d
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: |
        Unique key for this logical mutation. Reuse it only when retrying the
        same operation. Maximum 100 characters.
      schema:
        type: string
        minLength: 1
        maxLength: 100
      example: account-create-client-1042
    RequestId:
      name: X-Request-Id
      in: header
      required: false
      description: Optional correlation ID used for API request billing and logs.
      schema:
        type: string
      example: onboarding-client-1042
    Page:
      name: page
      in: query
      required: false
      description: One-based page number.
      schema:
        type: integer
        minimum: 1
        default: 1
    PageLimit:
      name: limit
      in: query
      required: false
      description: Maximum records returned on one page.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
  requestBodies:
    AccountAction:
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/AccountActionRequest"
          example:
            requestId: account-action-client-1042
    PasswordReset:
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PasswordResetRequest"
          example:
            requestId: password-reset-client-1042
    FinanceAction:
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/FinanceRequest"
          example:
            amount: 5000
            description: Evaluation fee credit
            requestId: deposit-client-1042
  responses:
    AcceptedCommand:
      description: Operation queued.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/CommandResponse"
    DuplicateCommand:
      description: An earlier command was returned for the idempotency key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/CommandResponse"
    PasswordCommand:
      description: Password operation queued or replayed. Store the returned password securely.
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/CommandResponse"
              - type: object
                required: [password]
                properties:
                  password:
                    type: string
                    description: New one-time password. This value is returned only with this response.
                    example: generated-secret-value
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Management API authentication is missing or invalid.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: Management API authentication required
    InsufficientCredits:
      description: The organization cannot cover the active API request price.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: Insufficient API credits
    Forbidden:
      description: The key lacks the required scope or server access.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: "Missing API scope: accounts:read"
    NotFound:
      description: The requested resource was not found.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Conflict:
      description: The request conflicts with the current resource state.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: Internal server error
  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: string
        issues:
          type: array
          description: Validation issue details when the request fails schema validation.
          items:
            type: object
            additionalProperties: true
      additionalProperties: false
    CreditWallet:
      type: object
      required:
        - id
        - organizationId
        - availableCredits
        - reservedCredits
        - lifetimePurchased
        - lifetimeConsumed
        - lowBalanceThreshold
        - createdAt
        - updatedAt
        - lowBalance
      properties:
        id: { type: string, format: uuid }
        organizationId: { type: string, format: uuid }
        availableCredits: { type: integer, minimum: 0 }
        reservedCredits: { type: integer, minimum: 0 }
        lifetimePurchased: { type: integer, minimum: 0 }
        lifetimeConsumed: { type: integer, minimum: 0 }
        lowBalanceThreshold: { type: integer, minimum: 0 }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        lowBalance: { type: boolean }
    ServerConfiguration:
      type: object
      required:
        - serverId
        - serverName
        - companyName
        - currency
        - currencySymbol
        - environment
        - supportedLeverage
        - defaultLeverage
        - status
      properties:
        serverId: { type: string }
        serverName: { type: string }
        companyName: { type: string }
        currency: { type: string, enum: [USD, NGN] }
        currencySymbol: { type: string }
        environment: { type: string, enum: [demo, real] }
        supportedLeverage:
          type: array
          minItems: 1
          items: { type: integer, minimum: 1 }
        defaultLeverage:
          oneOf:
            - type: integer
              minimum: 1
            - type: "null"
        status: { type: string, enum: [active, disabled] }
    UpdateServerConfigurationRequest:
      type: object
      required: [supportedLeverage, currency]
      properties:
        supportedLeverage:
          type: array
          minItems: 1
          maxItems: 100
          items: { type: integer, minimum: 1 }
        defaultLeverage:
          oneOf:
            - type: integer
              minimum: 1
            - type: "null"
        currency: { type: string, enum: [USD, NGN] }
      additionalProperties: false
    CreateAccountRequest:
      type: object
      required: [accountName, initialBalance, leverage, requestId]
      properties:
        accountName:
          type: string
          minLength: 1
          maxLength: 100
        password:
          type: string
          format: password
          minLength: 8
          maxLength: 128
          description: Omit to generate a password.
        initialBalance:
          type: number
          minimum: 0
          maximum: 1000000000
        leverage:
          type: integer
          minimum: 1
          maximum: 10000
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      additionalProperties: false
    CreateAccountResponse:
      type: object
      required: [command, duplicate]
      properties:
        command:
          $ref: "#/components/schemas/Command"
        credentials:
          type: object
          description: Returned only for a newly queued account creation.
          required: [tradingPassword, investorPassword]
          properties:
            tradingPassword:
              type: string
              description: One-time trading password returned with a newly queued account creation.
            investorPassword:
              type: string
              description: One-time investor password returned with a newly queued account creation.
        duplicate:
          type: boolean
    UpdateAccountRequest:
      type: object
      required: [requestId]
      properties:
        accountName:
          type: string
          minLength: 1
          maxLength: 100
        leverage:
          type: integer
          minimum: 1
          maximum: 10000
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      anyOf:
        - required: [accountName]
        - required: [leverage]
      additionalProperties: false
    AccountActionRequest:
      type: object
      required: [requestId]
      properties:
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      additionalProperties: false
    PasswordResetRequest:
      type: object
      required: [requestId]
      properties:
        password:
          type: string
          format: password
          minLength: 8
          maxLength: 128
          description: Omit to generate a password.
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      additionalProperties: false
    FinanceRequest:
      type: object
      required: [amount, requestId]
      properties:
        amount:
          type: number
          exclusiveMinimum: 0
          maximum: 1000000000
        description:
          type: string
          maxLength: 250
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      additionalProperties: false
    HistoryExportRequest:
      type: object
      required: [requestId]
      properties:
        from: { type: string, format: date-time }
        to: { type: string, format: date-time }
        limit:
          type: integer
          minimum: 1
          maximum: 1000
          default: 1000
        requestId:
          type: string
          minLength: 1
          maxLength: 100
      additionalProperties: false
    TradingAccount:
      type: object
      description: Known management and live fields for a trading account.
      required: [id, serverId, accountNumber, accountName, leverage, status, createdAt, updatedAt]
      properties:
        id: { type: string, format: uuid }
        organizationId: { type: string, format: uuid }
        serverId: { type: string, format: uuid }
        accountNumber: { type: string }
        accountName: { type: string }
        initialBalance: { type: number }
        balance: { type: number }
        leverage: { type: integer }
        status: { type: string, enum: [pending, active, disabled, archived, failed] }
        lastCommandId:
          oneOf:
            - type: string
              format: uuid
            - type: "null"
        currency: { type: string }
        currencySymbol: { type: string }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
      additionalProperties: true
    AccountDetailsResponse:
      type: object
      description: Account details and credentials. Historical records use separate paginated endpoints.
      required: [account, credentials]
      properties:
        account:
          $ref: "#/components/schemas/TradingAccount"
        credentials:
          type: object
          required: [tradingPassword, investorPassword]
          properties:
            tradingPassword:
              oneOf:
                - type: string
                - type: "null"
              description: Decrypted trading password when one is available.
            investorPassword:
              oneOf:
                - type: string
                - type: "null"
              description: Decrypted investor password when one is available.
    Pagination:
      type: object
      required: [page, limit, total, totalPages]
      properties:
        page: { type: integer, minimum: 1 }
        limit: { type: integer, minimum: 1, maximum: 100 }
        total: { type: integer, minimum: 0 }
        totalPages: { type: integer, minimum: 0 }
    TransactionPage:
      type: object
      required: [transactions, pagination]
      properties:
        transactions:
          type: array
          items: { $ref: "#/components/schemas/Transaction" }
        pagination: { $ref: "#/components/schemas/Pagination" }
    OrderPage:
      type: object
      required: [orders, pagination]
      properties:
        orders:
          type: array
          items: { $ref: "#/components/schemas/TradingOrder" }
        pagination: { $ref: "#/components/schemas/Pagination" }
    PositionPage:
      type: object
      required: [positions, pagination]
      properties:
        positions:
          type: array
          items: { $ref: "#/components/schemas/TradingPosition" }
        pagination: { $ref: "#/components/schemas/Pagination" }
    DealPage:
      type: object
      required: [deals, pagination]
      properties:
        deals:
          type: array
          items: { $ref: "#/components/schemas/TradingDeal" }
        pagination: { $ref: "#/components/schemas/Pagination" }
    TradingOrder:
      type: object
      description: Order record. Execution time is distinct from order creation time.
      properties:
        id: { type: string }
        symbol: { type: string }
        side: { type: string }
        orderType: { type: string }
        volume: { type: number }
        status: { type: string }
        createdAt:
          type: string
          format: date-time
          description: Date and time the order record was created, not webhook delivery time.
        executedAt:
          oneOf:
            - type: string
              format: date-time
            - type: "null"
          description: Date and time of execution, when the order has executed.
      additionalProperties: true
    TradingPosition:
      type: object
      description: Position record. Use linked deals for the execution time of each partial close.
      properties:
        id: { type: string }
        orderId: { type: string }
        symbol: { type: string }
        side: { type: string }
        volume: { type: number }
        openPrice: { type: number }
        status: { type: string }
        openedAt:
          type: string
          format: date-time
          description: Date and time the position opened.
        closedAt:
          oneOf:
            - type: string
              format: date-time
            - type: "null"
          description: Date and time the position fully closed. Null while still open, including after a partial close.
      additionalProperties: true
    TradingDeal:
      type: object
      description: An individual execution, including an entry, exit, or partial close.
      properties:
        id: { type: string }
        orderId: { type: string }
        positionId: { type: string }
        symbol: { type: string }
        side: { type: string }
        kind: { type: string }
        volume: { type: number }
        price: { type: number }
        commission: { type: number }
        realizedPnl: { type: number }
        swap: { type: number }
        executedAt:
          type: string
          format: date-time
          description: Date and time this execution occurred; preserved independently for every partial close.
      additionalProperties: true
    Transaction:
      type: object
      properties:
        referenceId: { type: string }
        type:
          type: string
          enum: [initial_balance, deposit, withdrawal, trade_profit, trade_loss, commission, swap, manual_adjustment]
        amount: { type: number }
        balanceBefore: { type: number }
        balanceAfter: { type: number }
        currency: { type: string }
        status: { type: string, enum: [completed, pending, failed, reversed] }
        description:
          oneOf:
            - type: string
            - type: "null"
        createdAt:
          type: string
          format: date-time
          description: Date and time this ledger entry was recorded, including deposits and withdrawals. Not external payment settlement or webhook delivery time.
      additionalProperties: true
    CommandResponse:
      type: object
      required: [command, duplicate]
      properties:
        command:
          $ref: "#/components/schemas/Command"
        duplicate:
          type: boolean
    Command:
      type: object
      required: [id, operation, status, attempts, idempotencyKey, createdAt]
      properties:
        id: { type: string, format: uuid }
        operation:
          type: string
          enum:
            - account.create
            - account.update
            - account.enable
            - account.disable
            - account.archive
            - account.delete
            - account.password_reset
            - account.investor_password_reset
            - account.history_export
            - finance.deposit
            - finance.withdraw
        status: { type: string, enum: [queued, leased, succeeded, failed, expired] }
        managedAccountId:
          oneOf:
            - type: string
              format: uuid
            - type: "null"
        attempts: { type: integer, minimum: 0 }
        payload:
          type: object
          additionalProperties: true
        result:
          oneOf:
            - type: object
              additionalProperties: true
            - type: "null"
        errorCode:
          oneOf:
            - type: string
            - type: "null"
        errorMessage:
          oneOf:
            - type: string
            - type: "null"
        idempotencyKey: { type: string }
        createdAt: { type: string, format: date-time }
        completedAt:
          oneOf:
            - type: string
              format: date-time
            - type: "null"
      additionalProperties: true
    AccountMetricSnapshot:
      type: object
      required:
        - id
        - organizationId
        - serverId
        - accountNumber
        - eventId
        - sequence
        - balance
        - equity
        - usedMargin
        - freeMargin
        - unrealizedProfit
        - dailyDrawdown
        - totalDrawdown
        - accountStatus
        - measuredAt
        - createdAt
      properties:
        id: { type: string, format: uuid }
        organizationId: { type: string, format: uuid }
        serverId: { type: string, format: uuid }
        accountNumber: { type: string }
        eventId: { type: string }
        sequence: { type: integer }
        balance: { type: number }
        equity: { type: number }
        usedMargin: { type: number }
        freeMargin: { type: number }
        marginLevel:
          oneOf:
            - type: number
            - type: "null"
        unrealizedProfit: { type: number }
        dailyDrawdown:
          type: number
          description: Not an authoritative organization drawdown calculation. Calculate rules from the account facts required by your policy.
        totalDrawdown:
          type: number
          description: Not an authoritative organization drawdown calculation. Do not interpret this value alone as proof of zero loss.
        accountStatus: { type: string }
        measuredAt:
          type: string
          format: date-time
          description: Source snapshot time. The existing sender rounds it to the minute; it does not certify price freshness or execution time.
        createdAt:
          type: string
          format: date-time
          description: Date and time the receiving service created the stored snapshot record.
