openapi: 3.0.3 info: title: Token Manager API description: | REST API untuk Sistem Monitoring Pemakaian Token AI. **Authentication:** - OTP flow (request-otp, verify-otp) tidak memerlukan API key - Semua endpoint lain memerlukan header `X-API-KEY` **Response format:** ```json { "status": "success|error", "message": "", "data": {} } ``` version: "1.0.0" contact: name: Token Manager servers: - url: https://ai.mygnbro.my.id/api/v1 description: Local MAMP Server components: securitySchemes: AuthKeyAuth: type: apiKey in: header name: X-AUTH-KEY description: "User authentication key (obtained from /auth/login) for managing keys, checking balance, and accessing logs." ApiKeyAuth: type: apiKey in: header name: X-API-KEY description: "API Key (created via /keys) used exclusively for AI model interactions and manual token deduction." schemas: SuccessResponse: type: object properties: status: type: string example: success message: type: string example: Operation successful data: type: object nullable: true ErrorResponse: type: object properties: status: type: string example: error message: type: string example: Error description data: nullable: true TokenBalance: type: object properties: token_limit: type: integer example: 10000 used: type: integer example: 3500 token_remaining: type: integer example: 6500 prepaid_balance: type: number format: float example: 50000.00 is_postpaid: type: boolean example: false reset_interval: type: string example: "monthly" next_reset: type: string example: "2026-03-01" TokenLog: type: object properties: id: type: integer user_id: type: integer model: type: string description: "Model alias used" example: "available-model" prompt_tokens: type: integer completion_tokens: type: integer total_tokens: type: integer example: 235 endpoint: type: string example: "/api/v1/chat/completions" ip_address: type: string example: "127.0.0.1" reference: type: string nullable: true example: "ord_12345" description: "Optional custom reference/flag for request tracing" created_at: type: string format: date-time ApiKey: type: object properties: id: type: integer name: type: string example: "My Production Key" api_key: type: string example: "tm_..." status: type: string example: "active" created_at: type: string format: date-time TokenSummaryModel: type: object properties: model: type: string example: "qwen25-7b" price_per_token: type: number example: 0.0001 request_count: type: integer example: 12 total_prompt_tokens: type: integer example: 1800 total_completion_tokens: type: integer example: 850 total_tokens: type: integer example: 2650 total_billed_tokens: type: integer example: 2000 total_cost_rp: type: number example: 0.2 TokenSummaryItem: type: object properties: api_key: type: string example: "tm_..." api_key_name: type: string example: "Production Key" total_tokens: type: integer example: 2650 total_billed_tokens: type: integer example: 2000 total_cost_rp: type: number example: 0.2 request_count: type: integer example: 12 models: type: array items: $ref: '#/components/schemas/TokenSummaryModel' User: type: object properties: id: type: integer name: type: string example: "John Doe" phone: type: string example: "628123456789" email: type: string nullable: true group_id: type: integer example: 2 status: type: string enum: [active, pending, inactive, banned] token_limit: type: integer example: 10000 reset_interval: type: string enum: [daily, weekly, monthly] created_at: type: string format: date-time responses: Unauthorized: description: API key missing or invalid content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: status: error message: API key required data: null Forbidden: description: Access denied - insufficient permissions content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: status: error message: Access denied data: null TooManyRequests: description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: status: error message: "Too many requests. Please try again in 45 seconds." data: null paths: # ========================================== # KEYS # ========================================== /keys: get: summary: List API Keys description: Mendapatkan daftar semua API key milik user. tags: [Keys] security: - AuthKeyAuth: [] responses: '200': description: List of API keys content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' - type: object properties: data: type: array items: $ref: '#/components/schemas/ApiKey' '401': $ref: '#/components/responses/Unauthorized' post: summary: Create API Key description: Membuat API key baru. tags: [Keys] security: - AuthKeyAuth: [] requestBody: content: application/json: schema: type: object properties: name: type: string example: "New Key Name" responses: '201': description: API key created content: application/json: schema: $ref: '#/components/schemas/SuccessResponse' /keys/{id}: delete: summary: Revoke API Key description: Menonaktifkan API key secara permanen. tags: [Keys] security: - AuthKeyAuth: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: API key revoked content: application/json: schema: $ref: '#/components/schemas/SuccessResponse' '404': description: Key not found '401': $ref: '#/components/responses/Unauthorized' # ========================================== # TOKEN # ========================================== /token/use: post: summary: Catat penggunaan token AI description: | Catat penggunaan token setelah request ke AI berhasil. Sistem akan memverifikasi sisa token sebelum mencatat. tags: [Token] security: - ApiKeyAuth: [] requestBody: required: true content: application/json: schema: type: object required: [model, messages] properties: model: type: string example: "available-model" description: "Alias model atau api_model dari /models" messages: type: array items: type: object required: [role, content] properties: role: { type: string, enum: [system, user, assistant] } content: { type: string } example: - role: system content: "You are a helpful assistant." - role: user content: "What is the capital of Indonesia?" mode_id: oneOf: - type: integer - type: string enum: [plain] description: | Opsional. ID Chat Mode dari `/chat/modes`. Gunakan `"plain"` untuk mengabaikan semua system prompt (Global Hidden Prompt & Chat Mode Prompt). example: 1 stream: type: boolean default: false max_tokens: type: integer example: 500 reference: type: string description: "Optional custom reference or flag to trace this request in logs." example: "my_trace_id_123" webhook_url: type: string format: uri description: "Optional URL to receive a POST notification once the message is complete. Useful for async integration." example: "https://my-app.com/webhooks/ai-complete" responses: '200': description: Token berhasil dicatat content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Tokens deducted successfully data: tokens_used: 235 remaining: 6265 '400': description: Field model atau token count tidak ada content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '402': description: Saldo token tidak cukup content: application/json: schema: allOf: - $ref: '#/components/schemas/ErrorResponse' example: status: error message: Insufficient token balance data: remaining: 50 required: 235 '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/TooManyRequests' /token/remaining: get: summary: Cek sisa token description: Mendapatkan total limit, jumlah terpakai, dan sisa token untuk periode reset aktif. tags: [Token] security: - AuthKeyAuth: [] responses: '200': description: Token balance content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Token balance retrieved data: total_limit: 10000 used: 3735 remaining: 6265 '401': $ref: '#/components/responses/Unauthorized' /token/history: get: summary: Riwayat penggunaan token description: Mendapatkan histori penggunaan token dengan pagination dan filter. tags: [Token] security: - AuthKeyAuth: [] parameters: - name: page in: query schema: type: integer default: 1 - name: per_page in: query schema: type: integer default: 20 - name: q in: query description: Search term (model, endpoint) schema: type: string - name: api_key in: query description: Filter by specific API Key string schema: type: string - name: start_date in: query description: Format YYYY-MM-DD schema: type: string - name: end_date in: query description: Format YYYY-MM-DD schema: type: string responses: '200': description: Token history with pagination content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Token history retrieved data: data: - id: 42 model: "available-model" prompt_tokens: 150 completion_tokens: 85 total_tokens: 235 endpoint: "/api/v1/chat/completions" created_at: "2026-02-26T05:00:00+07:00" total: 120 page: 1 per_page: 20 '401': $ref: '#/components/responses/Unauthorized' /token/summary: get: summary: Resume pemakaian token per API Key description: | Mendapatkan resume penggunaan token yang diagregasi per API key dan model, beserta total token, harga per token, total biaya (Rp), dan jumlah request. tags: [Token] security: - AuthKeyAuth: [] parameters: - name: q in: query description: Search term (model name, API key name) schema: type: string - name: api_key in: query description: Filter by specific API Key string schema: type: string - name: start_date in: query description: Format YYYY-MM-DD schema: type: string - name: end_date in: query description: Format YYYY-MM-DD schema: type: string responses: '200': description: Token usage summary grouped by API key content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Token usage summary retrieved data: summary: - api_key: "tm_abc123" api_key_name: "Production Key" total_tokens: 5200 total_billed_tokens: 3000 total_cost_rp: 0.30 request_count: 22 models: - model: "available-model" price_per_token: 0.0001 request_count: 12 total_prompt_tokens: 1800 total_completion_tokens: 850 total_tokens: 2650 total_billed_tokens: 2000 total_cost_rp: 0.20 - model: "available-model-2" price_per_token: 0.0001 request_count: 10 total_prompt_tokens: 1400 total_completion_tokens: 1150 total_tokens: 2550 total_billed_tokens: 1000 total_cost_rp: 0.10 grand_total: total_tokens: 5200 total_billed_tokens: 3000 total_cost_rp: 0.30 request_count: 22 '401': $ref: '#/components/responses/Unauthorized' /chat/modes: get: summary: List Chat Modes description: Mendapatkan daftar Chat Mode (pilihan karakter/prompt) yang aktif. tags: [Chat] security: - AuthKeyAuth: [] responses: '200': description: Chat modes list content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Active chat modes retrieved data: - id: 1 name: "Formal Assistant" sort_order: 1 is_active: 1 is_default: 1 - id: 2 name: "Code Expert" sort_order: 2 is_active: 1 is_default: 0 # ========================================== # MODELS # ========================================== /models: get: summary: List Model dan Harga description: Mendapatkan daftar model AI yang aktif beserta harga rate per token. tags: [Models] security: - AuthKeyAuth: [] responses: '200': description: Model list content: application/json: schema: allOf: - $ref: '#/components/schemas/SuccessResponse' example: status: success message: Models and token rates retrieved data: - name: "qwen25-7b" display_name: "Qwen 2.5 7B" price_per_token: 0.0001 tags: - name: Token description: Monitoring dan pencatatan penggunaan token AI - name: Models description: Informasi model LLM yang tersedia dan tarifnya