openapi: 3.0.3 info: title: 'Laravel API Documentation' description: '' version: 1.0.0 servers: - url: 'https://100.53.191.230' tags: - name: 'API Key Management' description: "\nAPIs for managing developer API keys." - name: Authentication description: "\nAPIs for handling user login, registration and logout." - name: Endpoints description: '' - name: Predictions description: "\nEndpoints for AI-powered repair cost predictions, VIN extraction, and prediction feedback.\nAll endpoints require a valid API key in the Authorization header." paths: /api/v1/dashboard/keys: get: summary: 'List API Keys' operationId: listAPIKeys description: 'Get a list of all API keys for the authenticated user.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: success data: - id: 1 name: 'My Key' public_key: pk_test_... secret_key: sk_test_... environment: test last_used_at: null properties: status: type: string example: success data: type: array example: - id: 1 name: 'My Key' public_key: pk_test_... secret_key: sk_test_... environment: test last_used_at: null items: type: object properties: id: type: integer example: 1 name: type: string example: 'My Key' public_key: type: string example: pk_test_... secret_key: type: string example: sk_test_... environment: type: string example: test last_used_at: type: string example: null nullable: true tags: - 'API Key Management' post: summary: 'Generate API Key' operationId: generateAPIKey description: 'Create a new API key for the authenticated user.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: success message: 'API key generated successfully...' data: id: 1 name: 'Mobile App' public_key: pk_test_... secret_key: sk_test_... environment: test properties: status: type: string example: success message: type: string example: 'API key generated successfully...' data: type: object properties: id: type: integer example: 1 name: type: string example: 'Mobile App' public_key: type: string example: pk_test_... secret_key: type: string example: sk_test_... environment: type: string example: test tags: - 'API Key Management' requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The name/label for the key.' example: 'Mobile App' environment: type: string description: 'The environment (live or test).' example: test required: - name - environment '/api/v1/dashboard/keys/{id}': delete: summary: 'Revoke API Key' operationId: revokeAPIKey description: 'Delete an API key, preventing any future access with it.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: success message: 'API key revoked successfully.' properties: status: type: string example: success message: type: string example: 'API key revoked successfully.' tags: - 'API Key Management' parameters: - in: path name: id description: 'The ID of the API key to revoke.' example: 1 required: true schema: type: integer /api/v1/auth/login: post: summary: 'Authenticate User' operationId: authenticateUser description: 'Log in a user with their email and password to receive a management token.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: success message: 'Authenticated successfully' data: user: id: 1 name: 'John Doe' session_token: 1|... properties: status: type: string example: success message: type: string example: 'Authenticated successfully' data: type: object properties: user: type: object properties: id: type: integer example: 1 name: type: string example: 'John Doe' session_token: type: string example: 1|... tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'The email of the user.' example: user@example.com password: type: string description: 'The password of the user.' example: password required: - email - password security: [] /api/v1/auth/register: post: summary: 'Register User' operationId: registerUser description: 'Create a new developer account and receive a management token.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: success message: 'User registered successfully' data: user: id: 1 name: 'John Doe' session_token: 2|... properties: status: type: string example: success message: type: string example: 'User registered successfully' data: type: object properties: user: type: object properties: id: type: integer example: 1 name: type: string example: 'John Doe' session_token: type: string example: 2|... tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The name of the user.' example: 'John Doe' email: type: string description: 'The email of the user.' example: john@example.com password: type: string description: 'The password (min 8 chars).' example: password required: - name - email - password security: [] /api/v1/auth/user/logout: post: summary: Logout operationId: logout description: 'Revoke the current management token.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: success message: 'Logged out successfully' properties: status: type: string example: success message: type: string example: 'Logged out successfully' tags: - Authentication /api/v1/admin/problems: get: summary: '' operationId: getApiV1AdminProblems description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/problems/stats: get: summary: '' operationId: getApiV1AdminProblemsStats description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] '/api/v1/admin/problems/{parentId}/children': get: summary: '' operationId: getApiV1AdminProblemsParentIdChildren description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] parameters: - in: path name: parentId description: '' example: architecto required: true schema: type: string '/api/v1/admin/problems/{id}/show': get: summary: '' operationId: getApiV1AdminProblemsIdShow description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] parameters: - in: path name: id description: 'The ID of the problem.' example: architecto required: true schema: type: string '/api/v1/admin/problems/{id}/details': get: summary: '' operationId: getApiV1AdminProblemsIdDetails description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] parameters: - in: path name: id description: 'The ID of the problem.' example: architecto required: true schema: type: string /api/v1/admin/vehicles: get: summary: '' operationId: getApiV1AdminVehicles description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] post: summary: '' operationId: postApiV1AdminVehicles description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: vehicle_model_id: type: string description: 'The id of an existing record in the vehicle_models table.' example: architecto nullable: true new_make: type: string description: 'This field is required when vehicle_model_id is not present. Must not be greater than 255 characters.' example: 'n' nullable: true new_model: type: string description: 'This field is required when vehicle_model_id is not present. Must not be greater than 255 characters.' example: g nullable: true new_year: type: integer description: 'This field is required when vehicle_model_id is not present. Must be at least 1900. Must not be greater than 2100.' example: 16 nullable: true vin_mask: type: string description: 'Must be at least 11 characters. Must not be greater than 17 characters.' example: miyvdlj current_mileage: type: integer description: 'Must be at least 0.' example: 52 plate_no: type: string description: 'Must not be greater than 20 characters.' example: ikhwaykcmyuwpwlv nullable: true motor_vehicle_type: type: string description: 'Must not be greater than 255 characters.' example: q nullable: true motor_vehicle_type_group: type: string description: 'Must not be greater than 255 characters.' example: w nullable: true seed_facility_id: type: string description: 'The id of an existing record in the service_facilities table.' example: null nullable: true required: - vin_mask - current_mileage security: [] /api/v1/admin/vehicles/stats: get: summary: '' operationId: getApiV1AdminVehiclesStats description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/vehicles/form-options: get: summary: '' operationId: getApiV1AdminVehiclesFormOptions description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/vehicles/search-models: get: summary: '' operationId: getApiV1AdminVehiclesSearchModels description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] '/api/v1/admin/vehicles/{vehicle_id}': get: summary: '' operationId: getApiV1AdminVehiclesVehicle_id description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] put: summary: '' operationId: putApiV1AdminVehiclesVehicle_id description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: vin_mask: type: string description: 'Must be at least 11 characters. Must not be greater than 17 characters.' example: bngzmiy current_mileage: type: integer description: 'Must be at least 0.' example: 60 plate_no: type: string description: 'Must not be greater than 20 characters.' example: dljnikhwaykcmyuw nullable: true motor_vehicle_type: type: string description: 'Must not be greater than 255 characters.' example: p nullable: true motor_vehicle_type_group: type: string description: 'Must not be greater than 255 characters.' example: w nullable: true required: - vin_mask - current_mileage security: [] delete: summary: '' operationId: deleteApiV1AdminVehiclesVehicle_id description: '' parameters: [] responses: { } tags: - Endpoints security: [] parameters: - in: path name: vehicle_id description: 'The ID of the vehicle.' example: 019ca9f7-adeb-731e-9b71-062301ab7efe required: true schema: type: string /api/v1/admin/regions: get: summary: '' operationId: getApiV1AdminRegions description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] post: summary: '' operationId: postApiV1AdminRegions description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: facility_name: type: string description: 'Must not be greater than 255 characters.' example: b city: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true state: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true zip_code: type: string description: 'Must not be greater than 20 characters.' example: zmiyvdljnikhwayk nullable: true country: type: string description: 'Must not be greater than 255 characters.' example: c nullable: true avg_labor_rate: type: number description: 'Must be at least 0.' example: 38 nullable: true required: - facility_name security: [] /api/v1/admin/regions/stats: get: summary: '' operationId: getApiV1AdminRegionsStats description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/regions/states: get: summary: '' operationId: getApiV1AdminRegionsStates description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/regions/recalculate: post: summary: '' operationId: postApiV1AdminRegionsRecalculate description: '' parameters: [] responses: { } tags: - Endpoints security: [] '/api/v1/admin/regions/{facility_id}': get: summary: '' operationId: getApiV1AdminRegionsFacility_id description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] put: summary: '' operationId: putApiV1AdminRegionsFacility_id description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: facility_name: type: string description: 'Must not be greater than 255 characters.' example: b city: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true state: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true zip_code: type: string description: 'Must not be greater than 20 characters.' example: zmiyvdljnikhwayk nullable: true country: type: string description: 'Must not be greater than 255 characters.' example: c nullable: true avg_labor_rate: type: number description: 'Must be at least 0.' example: 38 nullable: true required: - facility_name security: [] delete: summary: '' operationId: deleteApiV1AdminRegionsFacility_id description: '' parameters: [] responses: { } tags: - Endpoints security: [] parameters: - in: path name: facility_id description: 'The ID of the facility.' example: 1 required: true schema: type: integer /api/v1/admin/transactions: get: summary: '' operationId: getApiV1AdminTransactions description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] post: summary: '' operationId: postApiV1AdminTransactions description: '' parameters: [] responses: { } tags: - Endpoints security: [] /api/v1/admin/transactions/stats: get: summary: '' operationId: getApiV1AdminTransactionsStats description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/admin/transactions/form-options: get: summary: '' operationId: getApiV1AdminTransactionsFormOptions description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] '/api/v1/admin/transactions/{transaction_id}': get: summary: '' operationId: getApiV1AdminTransactionsTransaction_id description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] put: summary: '' operationId: putApiV1AdminTransactionsTransaction_id description: '' parameters: [] responses: { } tags: - Endpoints security: [] delete: summary: '' operationId: deleteApiV1AdminTransactionsTransaction_id description: '' parameters: [] responses: { } tags: - Endpoints security: [] parameters: - in: path name: transaction_id description: 'The ID of the transaction.' example: 1 required: true schema: type: integer '/api/v1/admin/transactions/{transaction_id}/details': get: summary: '' operationId: getApiV1AdminTransactionsTransaction_idDetails description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] parameters: - in: path name: transaction_id description: 'The ID of the transaction.' example: 1 required: true schema: type: integer /api/v1/secure-data: post: summary: 'Access Secure Data' operationId: accessSecureData description: "This endpoint is protected by the API Key middleware.\nYou must provide a valid secret key in the Authorization header." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Secure data accessed successfully' api_key_used: 'My Key' properties: message: type: string example: 'Secure data accessed successfully' api_key_used: type: string example: 'My Key' tags: - Endpoints /api/user: get: summary: '' operationId: getApiUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: message: Unauthenticated. properties: message: type: string example: Unauthenticated. tags: - Endpoints security: [] /api/v1/predict: post: summary: 'Get Repair Cost Prediction' operationId: getRepairCostPrediction description: "Calculates a repair price estimate using local historical data combined with\nAI predictions from Gemini and DeepSeek. Results are cached for 3 days using\na memory decay system (100% fresh → 0% at expiry)." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true session_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 memory_source: fresh memory_decay_remaining: 100.0 memory_expires_at: '2026-03-08T12:00:00+00:00' red_flag: false red_flag_reason: null match_percentage: 87.5 aggregated_ai_average: 450.0 local_prediction: average_total_estimate: 425.5 low_total_estimate: 350.0 high_total_estimate: 500.0 gemini_prediction: ai_average_estimate: 460.0 match_percentage: 92.0 deepseek_prediction: ai_average_estimate: 440.0 match_percentage: 96.5 repair_breakdown: source: local_scaled parts: - name: 'Brake Pad Set' unit_price: 45.0 quantity: 2 total_price: 90.0 estimated_labor_hours: 1.5 total_parts_cost: 90.0 total_estimate: 425.5 properties: success: type: boolean example: true session_id: type: string example: a1b2c3d4-e5f6-7890-abcd-ef1234567890 memory_source: type: string example: fresh memory_decay_remaining: type: number example: 100.0 memory_expires_at: type: string example: '2026-03-08T12:00:00+00:00' red_flag: type: boolean example: false red_flag_reason: type: string example: null nullable: true match_percentage: type: number example: 87.5 aggregated_ai_average: type: number example: 450.0 local_prediction: type: object properties: average_total_estimate: type: number example: 425.5 low_total_estimate: type: number example: 350.0 high_total_estimate: type: number example: 500.0 gemini_prediction: type: object properties: ai_average_estimate: type: number example: 460.0 match_percentage: type: number example: 92.0 deepseek_prediction: type: object properties: ai_average_estimate: type: number example: 440.0 match_percentage: type: number example: 96.5 repair_breakdown: type: object properties: source: type: string example: local_scaled parts: type: array example: - name: 'Brake Pad Set' unit_price: 45 quantity: 2 total_price: 90 items: type: object properties: name: type: string example: 'Brake Pad Set' unit_price: type: number example: 45.0 quantity: type: integer example: 2 total_price: type: number example: 90.0 estimated_labor_hours: type: number example: 1.5 total_parts_cost: type: number example: 90.0 total_estimate: type: number example: 425.5 404: description: '' content: application/json: schema: type: object example: success: false message: 'Vehicle not found based on the provided VIN.' error_code: VEHICLE_NOT_FOUND properties: success: type: boolean example: false message: type: string example: 'Vehicle not found based on the provided VIN.' error_code: type: string example: VEHICLE_NOT_FOUND 500: description: '' content: application/json: schema: type: object example: success: false message: 'An error occurred while calculating the estimate.' error_details: 'Error message here' properties: success: type: boolean example: false message: type: string example: 'An error occurred while calculating the estimate.' error_details: type: string example: 'Error message here' tags: - Predictions requestBody: required: true content: application/json: schema: type: object properties: vin: type: string description: 'The vehicle VIN (11 or 17 characters).' example: 1HGBH41JXMN109186 zip_code: type: string description: 'The zip code for regional pricing.' example: '90210' problem_identifier: type: string description: 'The problem category ID or identifier.' example: '42' required: - vin - zip_code - problem_identifier /api/v1/predict/problems: post: summary: 'Get Problem Identifiers' operationId: getProblemIdentifiers description: "Fetches available problem categories for a vehicle's group based on its VIN.\nReturns hierarchical category names (Parent > Child) that can be used as the\n`problem_identifier` parameter in the predict endpoint." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true data: - id: 42 display_name: 'Brakes > Brake Pad Replacement' - id: 15 display_name: 'Engine > Oil Change' - id: 78 display_name: 'Suspension > Shock Absorber' properties: success: type: boolean example: true data: type: array example: - id: 42 display_name: 'Brakes > Brake Pad Replacement' - id: 15 display_name: 'Engine > Oil Change' - id: 78 display_name: 'Suspension > Shock Absorber' items: type: object properties: id: type: integer example: 42 display_name: type: string example: 'Brakes > Brake Pad Replacement' 404: description: '' content: application/json: schema: type: object example: success: false message: 'Vehicle not found based on the provided VIN.' error_code: VEHICLE_NOT_FOUND properties: success: type: boolean example: false message: type: string example: 'Vehicle not found based on the provided VIN.' error_code: type: string example: VEHICLE_NOT_FOUND 500: description: '' content: application/json: schema: type: object example: success: false message: 'An error occurred while fetching problems.' error_details: 'Error message here' properties: success: type: boolean example: false message: type: string example: 'An error occurred while fetching problems.' error_details: type: string example: 'Error message here' tags: - Predictions requestBody: required: true content: application/json: schema: type: object properties: vin: type: string description: 'The vehicle VIN (11 or 17 characters).' example: 1HGBH41JXMN109186 required: - vin /api/v1/predict/categories: get: summary: 'List Problem Categories (Main)' operationId: listProblemCategoriesMain description: 'Returns all top-level (parent) problem identifier categories.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true data: - id: 1 name: Brakes description: 'Brake system issues' - id: 2 name: Engine description: 'Engine related problems' properties: success: type: boolean example: true data: type: array example: - id: 1 name: Brakes description: 'Brake system issues' - id: 2 name: Engine description: 'Engine related problems' items: type: object properties: id: type: integer example: 1 name: type: string example: Brakes description: type: string example: 'Brake system issues' tags: - Predictions '/api/v1/predict/categories/{parentId}/sub': get: summary: 'List Sub-Categories' operationId: listSubCategories description: 'Returns all child problem identifier categories under a given main category.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true data: - id: 10 name: 'Brake Pad Replacement' description: null - id: 11 name: 'Brake Rotor Replacement' description: null properties: success: type: boolean example: true data: type: array example: - id: 10 name: 'Brake Pad Replacement' description: null - id: 11 name: 'Brake Rotor Replacement' description: null items: type: object properties: id: type: integer example: 10 name: type: string example: 'Brake Pad Replacement' description: type: string example: null nullable: true 404: description: '' content: application/json: schema: type: object example: success: false message: 'Main category not found.' properties: success: type: boolean example: false message: type: string example: 'Main category not found.' tags: - Predictions parameters: - in: path name: parentId description: 'The ID of the main category.' example: 1 required: true schema: type: integer /api/v1/predict/red-flags: get: summary: 'Get Red Flags / Prediction History' operationId: getRedFlagsPredictionHistory description: "Returns the most recent 50 predictions with red flag analysis and feedback counts.\nAdmins see all predictions; regular users see only their own." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true data: - id: 1 session_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 vin: 1HGBH41JXMN109186 problem_name: 'Brakes > Brake Pad Replacement' red_flag: true red_flag_reason: 'Local estimate appears unrealistically low compared to the aggregated AI prediction.' match_percentage: 45.2 local_average_estimate: 150.0 ai_average_estimate: 450.0 feedbacks_count: 3 good_feedbacks_count: 2 created_at: '2026-03-05T10:30:00.000000Z' properties: success: type: boolean example: true data: type: array example: - id: 1 session_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 vin: 1HGBH41JXMN109186 problem_name: 'Brakes > Brake Pad Replacement' red_flag: true red_flag_reason: 'Local estimate appears unrealistically low compared to the aggregated AI prediction.' match_percentage: 45.2 local_average_estimate: 150 ai_average_estimate: 450 feedbacks_count: 3 good_feedbacks_count: 2 created_at: '2026-03-05T10:30:00.000000Z' items: type: object properties: id: type: integer example: 1 session_id: type: string example: a1b2c3d4-e5f6-7890-abcd-ef1234567890 vin: type: string example: 1HGBH41JXMN109186 problem_name: type: string example: 'Brakes > Brake Pad Replacement' red_flag: type: boolean example: true red_flag_reason: type: string example: 'Local estimate appears unrealistically low compared to the aggregated AI prediction.' match_percentage: type: number example: 45.2 local_average_estimate: type: number example: 150.0 ai_average_estimate: type: number example: 450.0 feedbacks_count: type: integer example: 3 good_feedbacks_count: type: integer example: 2 created_at: type: string example: '2026-03-05T10:30:00.000000Z' tags: - Predictions /api/v1/predict/extract-vin: post: summary: 'Extract VIN from Image' operationId: extractVINFromImage description: "Uses Gemini AI vision to extract a 17-character VIN from an uploaded image.\nSupports photos of VIN plates, stickers, dashboards, and registration documents." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true vin: 1HGBH41JXMN109186 message: 'VIN extracted successfully' properties: success: type: boolean example: true vin: type: string example: 1HGBH41JXMN109186 message: type: string example: 'VIN extracted successfully' 422: description: '' content: application/json: schema: type: object example: success: false message: 'Could not detect a valid 17-character VIN in the provided image.' properties: success: type: boolean example: false message: type: string example: 'Could not detect a valid 17-character VIN in the provided image.' 500: description: '' content: application/json: schema: type: object example: success: false message: 'An error occurred during AI image processing.' error_details: 'Error message here' properties: success: type: boolean example: false message: type: string example: 'An error occurred during AI image processing.' error_details: type: string example: 'Error message here' tags: - Predictions requestBody: required: true content: multipart/form-data: schema: type: object properties: image: type: string format: binary description: 'The image file containing a VIN. Max 10MB. Allowed types: jpeg, png, jpg, webp.' required: - image /api/v1/predict/feedback: post: summary: 'Submit Prediction Feedback' operationId: submitPredictionFeedback description: "Allows users to rate a prediction as \"good\" or \"wrong\" with an optional comment.\nAdmins can submit feedback on any prediction; regular users only on their own." parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: success: true message: 'Feedback submitted successfully.' data: id: 1 prediction_history_id: 42 rating: good comment: 'Price was accurate for my area.' created_at: '2026-03-05T12:00:00.000000Z' properties: success: type: boolean example: true message: type: string example: 'Feedback submitted successfully.' data: type: object properties: id: type: integer example: 1 prediction_history_id: type: integer example: 42 rating: type: string example: good comment: type: string example: 'Price was accurate for my area.' created_at: type: string example: '2026-03-05T12:00:00.000000Z' 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: session_id: - 'The selected session id is invalid.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: session_id: type: array example: - 'The selected session id is invalid.' items: type: string tags: - Predictions requestBody: required: true content: application/json: schema: type: object properties: session_id: type: string description: 'The UUID session ID of the prediction.' example: a1b2c3d4-e5f6-7890-abcd-ef1234567890 rating: type: string description: 'The rating value. Must be "good" or "wrong".' example: good comment: type: string description: 'Optional feedback comment (max 1000 chars).' example: 'Price was accurate for my area.' nullable: true required: - session_id - rating '/api/v1/predict/feedback/{session_id}': get: summary: 'Get Feedback Stats' operationId: getFeedbackStats description: "Returns feedback statistics and all feedback entries for a prediction.\nIncludes total count, good/wrong breakdown, and a calculated feedback score (0-100%).\nAdmins can view any prediction's feedback; regular users only their own." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true session_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 total_feedbacks: 5 good_count: 4 wrong_count: 1 feedback_score: 80.0 feedbacks: - id: 1 rating: good comment: 'Very accurate!' created_at: '2026-03-05T12:00:00.000000Z' properties: success: type: boolean example: true session_id: type: string example: a1b2c3d4-e5f6-7890-abcd-ef1234567890 total_feedbacks: type: integer example: 5 good_count: type: integer example: 4 wrong_count: type: integer example: 1 feedback_score: type: number example: 80.0 feedbacks: type: array example: - id: 1 rating: good comment: 'Very accurate!' created_at: '2026-03-05T12:00:00.000000Z' items: type: object properties: id: type: integer example: 1 rating: type: string example: good comment: type: string example: 'Very accurate!' created_at: type: string example: '2026-03-05T12:00:00.000000Z' 404: description: '' content: application/json: schema: type: object example: message: 'No query results for model [App\Models\PredictionHistory].' properties: message: type: string example: 'No query results for model [App\Models\PredictionHistory].' tags: - Predictions parameters: - in: path name: session_id description: 'The UUID session ID of the prediction.' example: a1b2c3d4-e5f6-7890-abcd-ef1234567890 required: true schema: type: string