diff --git a/app/Http/Controllers/Api/WorkerProfileController.php b/app/Http/Controllers/Api/WorkerProfileController.php index 916cb71..3af8bc4 100644 --- a/app/Http/Controllers/Api/WorkerProfileController.php +++ b/app/Http/Controllers/Api/WorkerProfileController.php @@ -30,6 +30,8 @@ public function getProfile(Request $request) $worker->load(['skills', 'documents']); + $worker->makeHidden(['email', 'religion', 'availability', 'bio', 'country', 'city', 'area']); + return response()->json([ 'success' => true, 'data' => [ @@ -49,16 +51,29 @@ public function updateProfile(Request $request) /** @var Worker $worker */ $worker = $request->attributes->get('worker'); + $data = $request->all(); + if (isset($data['passport']) && is_string($data['passport'])) { + $decoded = json_decode($data['passport'], true); + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { + $data['passport'] = $decoded; + } + } + if (isset($data['visa']) && is_string($data['visa'])) { + $decoded = json_decode($data['visa'], true); + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { + $data['visa'] = $decoded; + } + } + $request->merge($data); + $validator = Validator::make($request->all(), [ 'name' => 'nullable|string|max:255', + 'phone' => 'nullable|string|max:50|unique:workers,phone,' . $worker->id, 'age' => 'nullable|integer|min:18|max:70', 'nationality' => 'nullable|string|max:100', 'language' => 'nullable|string', 'salary' => 'nullable|numeric|min:0', - 'availability' => 'nullable|string|max:100', 'experience' => 'nullable|string|max:100', - 'religion' => 'nullable|string|max:100', - 'bio' => 'nullable|string|max:1000', 'skills' => 'nullable|array', 'skills.*' => 'exists:skills,id', 'in_country' => 'nullable', @@ -67,10 +82,19 @@ public function updateProfile(Request $request) 'gender' => 'nullable|string|in:male,female,other', 'live_in_out' => 'nullable|string|in:live_in,live_out', 'preferred_location' => 'nullable|string|max:255', - 'country' => 'nullable|string|max:100', - 'city' => 'nullable|string|max:100', - 'area' => 'nullable|string|max:100', 'fcm_token' => 'nullable|string|max:255', + 'passport' => 'nullable|array', + 'passport.passport_number' => [ + 'nullable', + 'string', + \Illuminate\Validation\Rule::unique('worker_documents', 'number')->where(function ($query) use ($worker) { + return $query->where('type', 'passport')->where('worker_id', '!=', $worker->id); + }), + ], + 'visa' => 'nullable|array', + ], [ + 'phone.unique' => 'This mobile number is already registered.', + 'passport.passport_number.unique' => 'This passport number is already registered.', ]); if ($validator->fails()) { @@ -81,27 +105,40 @@ public function updateProfile(Request $request) ], 422); } + // Document Update Check: Verify passport number matches existing passport number if already verified/saved + $existingPassport = $worker->documents()->where('type', 'passport')->first(); + if ($existingPassport && $request->has('passport')) { + $passportData = $request->input('passport'); + if ($passportData) { + $newPassportNumber = $passportData['passport_number'] ?? $passportData['number'] ?? null; + if ($newPassportNumber && strcasecmp(trim($existingPassport->number), trim($newPassportNumber)) !== 0) { + return response()->json([ + 'success' => false, + 'message' => 'Validation error.', + 'errors' => [ + 'passport.passport_number' => ['The passport number cannot be changed. It must match the previously verified passport number.'] + ] + ], 422); + } + } + } + try { - DB::transaction(function () use ($request, $worker) { + DB::transaction(function () use ($request, $worker, $existingPassport) { // Update worker attributes $updateData = $request->only([ 'name', + 'phone', 'age', 'nationality', 'language', 'salary', - 'availability', 'experience', - 'religion', - 'bio', 'visa_status', 'preferred_job_type', 'gender', 'live_in_out', 'preferred_location', - 'country', - 'city', - 'area', 'fcm_token' ]); @@ -120,10 +157,89 @@ public function updateProfile(Request $request) if ($request->has('skills')) { $worker->skills()->sync($request->skills ?: []); } + + // Process passport document creation/update + if ($request->has('passport')) { + $passportDataInput = $request->input('passport'); + if ($passportDataInput) { + $expiryDate = $passportDataInput['date_of_expiry'] ?? null; + $issueDate = $passportDataInput['date_of_issue'] ?? null; + $expiryDateFormatted = $expiryDate ? $this->normaliseDateForController($expiryDate) : null; + $issueDateFormatted = $issueDate ? $this->normaliseDateForController($issueDate) : null; + + if ($existingPassport) { + $updateFields = [ + 'ocr_data' => $passportDataInput, + ]; + if ($expiryDateFormatted) { + $updateFields['expiry_date'] = $expiryDateFormatted; + } + if ($issueDateFormatted) { + $updateFields['issue_date'] = $issueDateFormatted; + } + $existingPassport->update($updateFields); + } else { + $passportNum = $passportDataInput['passport_number'] ?? $passportDataInput['number'] ?? ('P' . rand(1000000, 9999999)); + $expiryDateFormatted = $expiryDateFormatted ?: now()->addYears(8)->toDateString(); + $issueDateFormatted = $issueDateFormatted ?: now()->subYears(3)->toDateString(); + + $worker->documents()->create([ + 'type' => 'passport', + 'number' => $passportNum, + 'issue_date' => $issueDateFormatted, + 'expiry_date' => $expiryDateFormatted, + 'ocr_accuracy' => 99.0, + 'file_path' => null, + 'ocr_data' => $passportDataInput, + ]); + } + } + } + + // Process visa document creation/update + if ($request->has('visa')) { + $visaDataInput = $request->input('visa'); + if ($visaDataInput) { + $existingVisa = $worker->documents()->where('type', 'visa')->first(); + $expiryDate = $visaDataInput['expiry_date'] ?? null; + $issueDate = $visaDataInput['issue_date'] ?? null; + $expiryDateFormatted = $expiryDate ? $this->normaliseDateForController($expiryDate) : null; + $issueDateFormatted = $issueDate ? $this->normaliseDateForController($issueDate) : null; + + if ($existingVisa) { + $updateFields = [ + 'ocr_data' => $visaDataInput, + ]; + if ($expiryDateFormatted) { + $updateFields['expiry_date'] = $expiryDateFormatted; + } + if ($issueDateFormatted) { + $updateFields['issue_date'] = $issueDateFormatted; + } + $existingVisa->update($updateFields); + } else { + $visaNum = $visaDataInput['file_number'] ?? $visaDataInput['id_number'] ?? ('V' . rand(1000000, 9999999)); + $expiryDateFormatted = $expiryDateFormatted ?: now()->addYears(2)->toDateString(); + $issueDateFormatted = $issueDateFormatted ?: now()->subYears(2)->toDateString(); + + $worker->documents()->create([ + 'type' => 'visa', + 'number' => $visaNum, + 'issue_date' => $issueDateFormatted, + 'expiry_date' => $expiryDateFormatted, + 'ocr_accuracy' => 99.0, + 'file_path' => null, + 'ocr_data' => $visaDataInput, + ]); + } + } + } }); $worker->load(['skills', 'documents']); + $worker->makeHidden(['email', 'religion', 'availability', 'bio', 'country', 'city', 'area']); + return response()->json([ 'success' => true, 'message' => 'Profile updated successfully.', @@ -552,4 +668,35 @@ public function getDashboard(Request $request) ], 500); } } + + /** + * E.g. "14/05/1990" -> "1990-05-14" + */ + private function normaliseDateForController(?string $raw): ?string + { + if (empty($raw)) { + return null; + } + $raw = trim($raw); + if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw)) { + return $raw; + } + try { + if (preg_match('/^(\d{1,2})[\/\- ]([A-Za-z0-9]+)[\/\- ](\d{4})$/', $raw, $m)) { + $day = str_pad($m[1], 2, '0', STR_PAD_LEFT); + $month = $m[2]; + $year = $m[3]; + if (is_numeric($month)) { + $month = str_pad($month, 2, '0', STR_PAD_LEFT); + return "{$year}-{$month}-{$day}"; + } + $ts = strtotime("{$day} {$month} {$year}"); + return $ts ? date('Y-m-d', $ts) : $raw; + } + $dt = new \DateTime($raw); + return $dt->format('Y-m-d'); + } catch (\Exception $e) { + return $raw; + } + } } diff --git a/public/swagger.json b/public/swagger.json index 68b5a94..01413da 100644 --- a/public/swagger.json +++ b/public/swagger.json @@ -1,5529 +1,5534 @@ { - "openapi": "3.0.0", - "info": { - "title": "Migrant Mobile API", - "description": "Comprehensive API endpoints for the Migrant Mobile Application, covering worker registration, password login, token authentication, profile management, and interactive job offer responses.", - "version": "1.0.0" - }, - "servers": [ + "openapi": "3.0.0", + "info": { + "title": "Migrant Mobile API", + "description": "Comprehensive API endpoints for the Migrant Mobile Application, covering worker registration, password login, token authentication, profile management, and interactive job offer responses.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/api", + "description": "Local development API prefix" + } + ], + "security": [ + { + "bearerAuth": [] + } + ], + "paths": { + "/sponsors/register": { + "post": { + "tags": [ + "Sponsor/Auth" + ], + "summary": "Register Sponsor Account", + "description": "Registers a new sponsor with basic information and uploads their organization/trade license. Sponsors have restricted access — dashboard and charity events only. No payment required.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "full_name", + "mobile", + "password", + "organization_name", + "email", + "nationality", + "city", + "address", + "country_code" + ], + "properties": { + "full_name": { + "type": "string", + "example": "Mohammed Al-Rashidi", + "description": "Full legal name of the sponsor. (REQUIRED)" + }, + "mobile": { + "type": "string", + "example": "+971501112233", + "description": "Mobile phone number — must be unique. (REQUIRED)" + }, + "password": { + "type": "string", + "format": "password", + "example": "securepass123", + "description": "Login password (min 6 characters). (REQUIRED)" + }, + "license": { + "type": "object", + "description": "Optional JSON object containing extracted organization/trade license details.", + "properties": { + "license_number": { + "type": "string", + "example": "123456" + }, + "organization_name": { + "type": "string", + "example": "Al-Rashidi Charitable Foundation" + }, + "expiry_date": { + "type": "string", + "example": "2028-06-12" + } + } + }, + "emirates_id": { + "type": "object", + "description": "Optional JSON object containing extracted Emirates ID details.", + "properties": { + "emirates_id_number": { + "type": "string", + "example": "784-1988-5310327-2" + }, + "name": { + "type": "string", + "example": "KRISHNA PRASAD" + }, + "nationality": { + "type": "string", + "example": "NPL" + }, + "date_of_birth": { + "type": "string", + "example": "1988-03-22" + }, + "expiry_date": { + "type": "string", + "example": "2028-04-11" + }, + "issue_date": { + "type": "string", + "example": "2023-04-11" + }, + "employer": { + "type": "string", + "example": "Federal Authority" + }, + "issue_place": { + "type": "string", + "example": "Abu Dhabi" + }, + "occupation": { + "type": "string", + "example": "Manager" + } + } + }, + "organization_name": { + "type": "string", + "example": "Al-Rashidi Charitable Foundation", + "description": "Name of the sponsoring organization. (REQUIRED)" + }, + "email": { + "type": "string", + "format": "email", + "example": "info@alrashidi.ae", + "description": "Email address (must be unique). (REQUIRED)" + }, + "nationality": { + "type": "string", + "example": "UAE", + "description": "Nationality of the sponsor. (REQUIRED)" + }, + "city": { + "type": "string", + "example": "Dubai", + "description": "City of residence. (REQUIRED)" + }, + "address": { + "type": "string", + "example": "Villa 12, Jumeirah 2", + "description": "Physical address. (REQUIRED)" + }, + "country_code": { + "type": "string", + "example": "+971", + "description": "Country dial code. (REQUIRED)" + }, + "license_expiry": { + "type": "string", + "format": "date", + "example": "2028-06-12", + "description": "Expiry date of the license (YYYY-MM-DD). (REQUIRED)" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Sponsor registered successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Sponsor account registered successfully. Your license is pending admin review." + }, + "data": { + "type": "object", + "properties": { + "sponsor": { + "$ref": "#/components/schemas/Sponsor" + }, + "token": { + "type": "string", + "example": "abc123...bearerToken..." + } + } + } + } + } + } + } + }, + "422": { + "description": "Validation error (duplicate mobile/email, missing required fields)." + } + } + } + }, + "/sponsors/register/license": { + "post": { + "tags": [ + "Sponsor/Auth" + ], + "summary": "Upload Sponsor Trade License", + "description": "Uploads and processes the trade license document of a Sponsor using OCR.", + "security": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "email", + "license_file" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "info@alrashidi.ae", + "description": "Email address of the registered sponsor. (REQUIRED)" + }, + "license_file": { + "type": "string", + "format": "binary", + "description": "Trade license image/PDF file. (REQUIRED)" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Trade license processed successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "License uploaded and processed successfully." + }, + "ocr_extracted_data": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "expiry_date": { + "type": "string", + "example": "2028-06-12" + }, + "license_number": { + "type": "string", + "example": "123456" + }, + "organization_name": { + "type": "string", + "example": "Charity Co" + } + } + }, + "email": { + "type": "string", + "example": "info@alrashidi.ae" + } + } + } + } + } + }, + "404": { + "description": "Sponsor account not found." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/sponsors/login": { + "post": { + "tags": [ + "Sponsor/Auth" + ], + "summary": "Authenticate Sponsor or Employer", + "description": "Unified authentication endpoint. Validates email/mobile and password, then returns a stateless Bearer token and the user's role (employer or sponsor). Same behavior as /employers/login.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "password" + ], + "properties": { + "email": { + "type": "string", + "example": "ahmad@example.com", + "description": "Email address (required if mobile is not provided)" + }, + "mobile": { + "type": "string", + "example": "+971509990001", + "description": "Mobile number (required if email is not provided)" + }, + "password": { + "type": "string", + "example": "Password@123" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Login successful. Returns Bearer token and role.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Login successful." + }, + "role": { + "type": "string", + "example": "sponsor", + "enum": [ + "employer", + "sponsor" + ] + }, + "data": { + "type": "object", + "properties": { + "token": { + "type": "string", + "example": "api_token_here" + } + } + } + } + } + } + } + } + } + } + }, + "/sponsors/dashboard": { + "get": { + "tags": [ + "Sponsor" + ], + "summary": "Sponsor Dashboard", + "description": "Returns the sponsor profile summary, latest charity events, and total & active counts for employers and workers.", + "responses": { + "200": { + "description": "Dashboard data retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "sponsor": { + "type": "object" + }, + "recent_charity_events": { + "type": "array", + "items": { + "type": "object" + } + }, + "total_events": { + "type": "integer" + }, + "employer_stats": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 12 + }, + "active": { + "type": "integer", + "example": 8 + } + } + }, + "worker_stats": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 150 + }, + "active": { + "type": "integer", + "example": 120 + } + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthenticated." + } + } + } + }, + "/sponsors/charity-events": { + "get": { + "tags": [ + "Sponsor" + ], + "summary": "List Charity Events", + "description": "Returns a paginated list of all charity and announcement events. Optionally filter by type.", + "parameters": [ { - "url": "/api", - "description": "Local development API prefix" + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 15 + } + }, + { + "name": "type", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Optional event type filter (e.g. 'charity', 'update')." } ], - "security": [ - { - "bearerAuth": [ - - ] - } - ], - "paths": { - "/sponsors/register": { - "post": { - "tags": [ - "Sponsor/Auth" - ], - "summary": "Register Sponsor Account", - "description": "Registers a new sponsor with basic information and uploads their organization/trade license. Sponsors have restricted access — dashboard and charity events only. No payment required.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "full_name", - "mobile", - "password", - "organization_name", - "email", - "nationality", - "city", - "address", - "country_code" - ], - "properties": { - "full_name": { - "type": "string", - "example": "Mohammed Al-Rashidi", - "description": "Full legal name of the sponsor. (REQUIRED)" - }, - "mobile": { - "type": "string", - "example": "+971501112233", - "description": "Mobile phone number — must be unique. (REQUIRED)" - }, - "password": { - "type": "string", - "format": "password", - "example": "securepass123", - "description": "Login password (min 6 characters). (REQUIRED)" - }, - "license": { - "type": "object", - "description": "Optional JSON object containing extracted organization/trade license details.", - "properties": { - "license_number": { - "type": "string", - "example": "123456" - }, - "organization_name": { - "type": "string", - "example": "Al-Rashidi Charitable Foundation" - }, - "expiry_date": { - "type": "string", - "example": "2028-06-12" - } - } - }, - "emirates_id": { - "type": "object", - "description": "Optional JSON object containing extracted Emirates ID details.", - "properties": { - "emirates_id_number": { - "type": "string", - "example": "784-1988-5310327-2" - }, - "name": { - "type": "string", - "example": "KRISHNA PRASAD" - }, - "nationality": { - "type": "string", - "example": "NPL" - }, - "date_of_birth": { - "type": "string", - "example": "1988-03-22" - }, - "expiry_date": { - "type": "string", - "example": "2028-04-11" - }, - "issue_date": { - "type": "string", - "example": "2023-04-11" - }, - "employer": { - "type": "string", - "example": "Federal Authority" - }, - "issue_place": { - "type": "string", - "example": "Abu Dhabi" - }, - "occupation": { - "type": "string", - "example": "Manager" - } - } - }, - "organization_name": { - "type": "string", - "example": "Al-Rashidi Charitable Foundation", - "description": "Name of the sponsoring organization. (REQUIRED)" - }, - "email": { - "type": "string", - "format": "email", - "example": "info@alrashidi.ae", - "description": "Email address (must be unique). (REQUIRED)" - }, - "nationality": { - "type": "string", - "example": "UAE", - "description": "Nationality of the sponsor. (REQUIRED)" - }, - "city": { - "type": "string", - "example": "Dubai", - "description": "City of residence. (REQUIRED)" - }, - "address": { - "type": "string", - "example": "Villa 12, Jumeirah 2", - "description": "Physical address. (REQUIRED)" - }, - "country_code": { - "type": "string", - "example": "+971", - "description": "Country dial code. (REQUIRED)" - }, - "license_expiry": { - "type": "string", - "format": "date", - "example": "2028-06-12", - "description": "Expiry date of the license (YYYY-MM-DD). (REQUIRED)" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Sponsor registered successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Sponsor account registered successfully. Your license is pending admin review." - }, - "data": { - "type": "object", - "properties": { - "sponsor": { - "$ref": "#/components/schemas/Sponsor" - }, - "token": { - "type": "string", - "example": "abc123...bearerToken..." - } - } - } - } - } - } - } - }, - "422": { - "description": "Validation error (duplicate mobile/email, missing required fields)." - } - } - } - }, - "/sponsors/register/license": { - "post": { - "tags": [ - "Sponsor/Auth" - ], - "summary": "Upload Sponsor Trade License", - "description": "Uploads and processes the trade license document of a Sponsor using OCR.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "email", - "license_file" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "info@alrashidi.ae", - "description": "Email address of the registered sponsor. (REQUIRED)" - }, - "license_file": { - "type": "string", - "format": "binary", - "description": "Trade license image/PDF file. (REQUIRED)" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Trade license processed successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "License uploaded and processed successfully." - }, - "ocr_extracted_data": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "expiry_date": { - "type": "string", - "example": "2028-06-12" - }, - "license_number": { - "type": "string", - "example": "123456" - }, - "organization_name": { - "type": "string", - "example": "Charity Co" - } - } - }, - "email": { - "type": "string", - "example": "info@alrashidi.ae" - } - } - } - } - } - }, - "404": { - "description": "Sponsor account not found." - }, - "422": { - "description": "Validation error." - } - } - } - }, - "/sponsors/login": { - "post": { - "tags": [ - "Sponsor/Auth" - ], - "summary": "Authenticate Sponsor or Employer", - "description": "Unified authentication endpoint. Validates email/mobile and password, then returns a stateless Bearer token and the user\u0027s role (employer or sponsor). Same behavior as /employers/login.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "password" - ], - "properties": { - "email": { - "type": "string", - "example": "ahmad@example.com", - "description": "Email address (required if mobile is not provided)" - }, - "mobile": { - "type": "string", - "example": "+971509990001", - "description": "Mobile number (required if email is not provided)" - }, - "password": { - "type": "string", - "example": "Password@123" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Login successful. Returns Bearer token and role.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Login successful." - }, - "role": { - "type": "string", - "example": "sponsor", - "enum": [ - "employer", - "sponsor" - ] - }, - "data": { - "type": "object", - "properties": { - "token": { - "type": "string", - "example": "api_token_here" - } - } - } - } - } - } - } - } - } - } - }, - "/sponsors/dashboard": { - "get": { - "tags": [ - "Sponsor" - ], - "summary": "Sponsor Dashboard", - "description": "Returns the sponsor profile summary, latest charity events, and total \u0026 active counts for employers and workers.", - "responses": { - "200": { - "description": "Dashboard data retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "data": { - "type": "object", - "properties": { - "sponsor": { - "type": "object" - }, - "recent_charity_events": { - "type": "array", - "items": { - "type": "object" - } - }, - "total_events": { - "type": "integer" - }, - "employer_stats": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 12 - }, - "active": { - "type": "integer", - "example": 8 - } - } - }, - "worker_stats": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 150 - }, - "active": { - "type": "integer", - "example": 120 - } - } - } - } - } - } - } - } - } - }, - "401": { - "description": "Unauthenticated." - } - } - } - }, - "/sponsors/charity-events": { - "get": { - "tags": [ - "Sponsor" - ], - "summary": "List Charity Events", - "description": "Returns a paginated list of all charity and announcement events. Optionally filter by type.", - "parameters": [ - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 15 - } - }, - { - "name": "type", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Optional event type filter (e.g. \u0027charity\u0027, \u0027update\u0027)." - } - ], - "responses": { - "200": { - "description": "Events retrieved successfully." - }, - "401": { - "description": "Unauthenticated." - } - } - }, - "post": { - "tags": [ - "Sponsor" - ], - "summary": "Post Charity Event (Sponsor)", - "description": "Creates and publishes a new charity event or community drive for the authenticated sponsor.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "title", - "body", - "event_date", - "start_time", - "end_time", - "provided_items", - "location_details", - "location_pin" - ], - "properties": { - "title": { - "type": "string", - "example": "Free Dental Checkup Drive" - }, - "body": { - "type": "string", - "example": "Emirates Charity is organizing a free dental checkup for all workers this Friday morning." - }, - "type": { - "type": "string", - "enum": [ - "charity", - "info", - "warning", - "success" - ], - "example": "charity" - }, - "event_date": { - "type": "string", - "format": "date", - "example": "2026-06-15" - }, - "start_time": { - "type": "string", - "example": "09:00 AM" - }, - "end_time": { - "type": "string", - "example": "04:00 PM" - }, - "provided_items": { - "type": "string", - "example": "Free Dental screening, cleanings, and wellness kits" - }, - "location_details": { - "type": "string", - "example": "Al Quoz Community Hall, Dubai" - }, - "location_pin": { - "type": "string", - "format": "uri", - "example": "https://maps.app.goo.gl/xyz" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Charity event posted successfully." - }, - "401": { - "description": "Unauthenticated." - }, - "422": { - "description": "Validation error." - } - } - } - }, - "/sponsors/profile": { - "get": { - "tags": [ - "Sponsor" - ], - "summary": "Get Sponsor Profile", - "description": "Returns the full profile of the authenticated sponsor.", - "responses": { - "200": { - "description": "Profile retrieved successfully." - }, - "401": { - "description": "Unauthenticated." - } - } + "responses": { + "200": { + "description": "Events retrieved successfully." + }, + "401": { + "description": "Unauthenticated." + } + } + }, + "post": { + "tags": [ + "Sponsor" + ], + "summary": "Post Charity Event (Sponsor)", + "description": "Creates and publishes a new charity event or community drive for the authenticated sponsor.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "title", + "body", + "event_date", + "start_time", + "end_time", + "provided_items", + "location_details", + "location_pin" + ], + "properties": { + "title": { + "type": "string", + "example": "Free Dental Checkup Drive" + }, + "body": { + "type": "string", + "example": "Emirates Charity is organizing a free dental checkup for all workers this Friday morning." + }, + "type": { + "type": "string", + "enum": [ + "charity", + "info", + "warning", + "success" + ], + "example": "charity" + }, + "event_date": { + "type": "string", + "format": "date", + "example": "2026-06-15" + }, + "start_time": { + "type": "string", + "example": "09:00 AM" + }, + "end_time": { + "type": "string", + "example": "04:00 PM" + }, + "provided_items": { + "type": "string", + "example": "Free Dental screening, cleanings, and wellness kits" + }, + "location_details": { + "type": "string", + "example": "Al Quoz Community Hall, Dubai" + }, + "location_pin": { + "type": "string", + "format": "uri", + "example": "https://maps.app.goo.gl/xyz" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Charity event posted successfully." + }, + "401": { + "description": "Unauthenticated." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/sponsors/profile": { + "get": { + "tags": [ + "Sponsor" + ], + "summary": "Get Sponsor Profile", + "description": "Returns the full profile of the authenticated sponsor.", + "responses": { + "200": { + "description": "Profile retrieved successfully." + }, + "401": { + "description": "Unauthenticated." + } + } + } + }, + "/workers/register": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Register Worker Account (Step 1)", + "description": "Creates the worker account and returns a secure Bearer token.\n\nDocument upload is a **separate step** after registration:\n- `POST /workers/register/passport` — upload passport (OCR-processed)\n- `POST /workers/register/visa` — upload visa (OCR-processed)\n\nBoth document endpoints require the Bearer token returned here.", + "security": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "name", + "phone", + "salary", + "password" + ], + "properties": { + "name": { + "type": "string", + "example": "Amina Al-Masry", + "description": "Full legal name of the worker. (REQUIRED)" + }, + "phone": { + "type": "string", + "example": "+971509998888", + "description": "Contact mobile phone number (must be unique). (REQUIRED)" + }, + "salary": { + "type": "number", + "format": "float", + "example": 1500, + "description": "Desired minimum monthly salary in AED. (REQUIRED)" + }, + "password": { + "type": "string", + "format": "password", + "example": "securepassword123", + "description": "Secret password for subsequent mobile logins (min 6 chars). (REQUIRED)" + }, + "skills": { + "type": "array", + "items": { + "type": "integer" + }, + "example": [ + 1, + 3 + ], + "description": "Optional array of Skill IDs (can be sent as comma-separated string e.g. '1,3' or json '[1,3]' in multipart)." + }, + "language": { + "type": "string", + "example": "English, Arabic", + "description": "Languages spoken by the worker (e.g. 'English, Arabic, Hindi')." + }, + "nationality": { + "type": "string", + "example": "Egypt", + "description": "Nationality of the worker." + }, + "age": { + "type": "integer", + "example": 28, + "description": "Age of the worker." + }, + "experience": { + "type": "string", + "example": "4 Years", + "description": "Years of experience." + }, + "in_country": { + "type": "boolean", + "example": true, + "description": "Whether the worker is currently in-country (true) or out-country (false)." + }, + "visa_status": { + "type": "string", + "example": "Tourist Visa", + "description": "Visa status (only required/applicable if in_country is true)." + }, + "preferred_job_type": { + "type": "string", + "example": "full-time", + "description": "Preferred job type." + }, + "live_in_out": { + "type": "string", + "enum": [ + "live_in", + "live_out" + ], + "example": "live_out", + "description": "Accommodation preference (live_in, live_out)." + }, + "gender": { + "type": "string", + "enum": [ + "male", + "female", + "other" + ], + "example": "male", + "description": "Gender of the worker (male, female, other)." + }, + "preferred_location": { + "type": "string", + "example": "Dubai Marina", + "description": "Preferred work location/area." + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + }, + "passport": { + "type": "object", + "description": "Optional JSON object containing extracted passport details.", + "properties": { + "authority": { + "type": "string" + }, + "date_of_birth": { + "type": "string", + "example": "14/05/1990" + }, + "date_of_expiry": { + "type": "string", + "example": "05/07/2022" + }, + "date_of_issue": { + "type": "string", + "example": "05/07/2017" + }, + "document_type": { + "type": "string" + }, + "given_names": { + "type": "string" + }, + "issuing_country": { + "type": "string" + }, + "nationality": { + "type": "string" + }, + "passport_number": { + "type": "string" + }, + "personal_number": { + "type": "string" + }, + "place_of_birth": { + "type": "string" + }, + "sex": { + "type": "string" + }, + "surname": { + "type": "string" + } + } + }, + "visa": { + "type": "object", + "description": "Optional JSON object containing extracted visa details.", + "properties": { + "accompanied_by": { + "type": "string" + }, + "expiry_date": { + "type": "string", + "example": "2024-02-15" + }, + "file_number": { + "type": "string" + }, + "id_number": { + "type": "string" + }, + "issue_date": { + "type": "string", + "example": "2022-02-16" + }, + "name": { + "type": "string" + }, + "passport_number": { + "type": "string" + }, + "place_of_issue": { + "type": "string" + }, + "profession": { + "type": "string" + }, + "sponsor": { + "type": "string" + } + } + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Worker registered successfully. Use the returned token for document upload steps.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Worker registered successfully. Please upload your passport and visa documents." + }, + "data": { + "type": "object", + "properties": { + "worker": { + "$ref": "#/components/schemas/Worker" + }, + "token": { + "type": "string", + "example": "abc123xyz456...secureToken..." + } + } + } + } + } + } + } + }, + "422": { + "description": "Validation error — field validation failed or Passport OCR extraction failed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "Validation error." + }, + "errors": { + "type": "object", + "properties": { + "phone": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "This mobile number is already registered." + ] + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "The name field is required." + ] + } + } + } + } + } + } + } + }, + "500": { + "description": "Server error during registration (e.g. database transaction failure).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "An error occurred during worker registration. Please try again." + }, + "error": { + "type": "string", + "example": "Internal Server Error" + } + } + } + } + } + } + } + } + }, + "/workers/login": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Authenticate Worker", + "description": "Validates worker credentials (mobile phone number and password) and generates a secure, stateless Bearer token for api access.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone", + "password" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971509998888", + "description": "Registered mobile phone number." + }, + "password": { + "type": "string", + "format": "password", + "example": "securepassword123", + "description": "Secret account password." + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Worker logged in successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Worker logged in successfully." + }, + "data": { + "type": "object", + "properties": { + "worker": { + "$ref": "#/components/schemas/Worker" + }, + "token": { + "type": "string", + "example": "abc123xyz456...secureToken..." + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized access.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "Invalid mobile number or password." + } + } + } + } + } + } + } + } + }, + "/workers/ocr/passport-vision": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Extract Passport Data using Google Cloud Vision API", + "description": "Upload a passport image file to extract OCR data using the Google Cloud Vision API.", + "security": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "passport_file" + ], + "properties": { + "passport_file": { + "type": "string", + "format": "binary", + "description": "Passport image file (jpg/png/jpeg, max 10MB)." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Passport data successfully extracted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "passport_number": { + "type": "string", + "example": "Z43R34255" + }, + "given_names": { + "type": "string", + "example": "AHMAD" + }, + "date_of_birth": { + "type": "string", + "example": "03/07/1978" + }, + "nationality": { + "type": "string", + "example": "ARE" + }, + "issuing_country": { + "type": "string", + "example": "ARE" + }, + "place_of_birth": { + "type": "string", + "example": "DUBAI" + }, + "date_of_issue": { + "type": "string", + "example": "10/02/2015" + }, + "date_of_expiry": { + "type": "string", + "example": "10/02/2020" + } + } + } + } + } + } + } + }, + "422": { + "description": "Validation error (missing passport_file)." + }, + "400": { + "description": "OCR processing error or failed Google Vision API request." + } + } + } + }, + "/workers/config": { + "get": { + "tags": [ + "Worker/Auth" + ], + "summary": "Get Supported Languages and Config", + "description": "Returns a list of supported regional languages (Hindi, Swahili, Tagalog, Tamil) for helper onboarding.", + "security": [], + "responses": { + "200": { + "description": "Config and supported languages retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "languages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "HI" + }, + "name": { + "type": "string", + "example": "Hindi (हिन्दी)" } + } + } + } + } + } + } + } + } + } + } + }, + "/workers/skills": { + "get": { + "tags": [ + "Worker/Auth" + ], + "summary": "Get Master Skills List", + "description": "Returns a list of all available master skills for helper onboarding profile setup.", + "security": [], + "responses": { + "200": { + "description": "Master skills list retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - "/workers/register": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Register Worker Account (Step 1)", - "description": "Creates the worker account and returns a secure Bearer token.\n\nDocument upload is a **separate step** after registration:\n- `POST /workers/register/passport` — upload passport (OCR-processed)\n- `POST /workers/register/visa` — upload visa (OCR-processed)\n\nBoth document endpoints require the Bearer token returned here.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "name", - "phone", - "salary", - "password" - ], - "properties": { - "name": { - "type": "string", - "example": "Amina Al-Masry", - "description": "Full legal name of the worker. (REQUIRED)" - }, - "phone": { - "type": "string", - "example": "+971509998888", - "description": "Contact mobile phone number (must be unique). (REQUIRED)" - }, - "salary": { - "type": "number", - "format": "float", - "example": 1500, - "description": "Desired minimum monthly salary in AED. (REQUIRED)" - }, - "password": { - "type": "string", - "format": "password", - "example": "securepassword123", - "description": "Secret password for subsequent mobile logins (min 6 chars). (REQUIRED)" - }, - "skills": { - "type": "array", - "items": { - "type": "integer" - }, - "example": [ - 1, - 3 - ], - "description": "Optional array of Skill IDs (can be sent as comma-separated string e.g. \u00271,3\u0027 or json \u0027[1,3]\u0027 in multipart)." - }, - "language": { - "type": "string", - "example": "English, Arabic", - "description": "Languages spoken by the worker (e.g. \u0027English, Arabic, Hindi\u0027)." - }, - "nationality": { - "type": "string", - "example": "Egypt", - "description": "Nationality of the worker." - }, - "age": { - "type": "integer", - "example": 28, - "description": "Age of the worker." - }, - "experience": { - "type": "string", - "example": "4 Years", - "description": "Years of experience." - }, - "in_country": { - "type": "boolean", - "example": true, - "description": "Whether the worker is currently in-country (true) or out-country (false)." - }, - "visa_status": { - "type": "string", - "example": "Tourist Visa", - "description": "Visa status (only required/applicable if in_country is true)." - }, - "preferred_job_type": { - "type": "string", - "example": "full-time", - "description": "Preferred job type." - }, - "live_in_out": { - "type": "string", - "enum": [ - "live_in", - "live_out" - ], - "example": "live_out", - "description": "Accommodation preference (live_in, live_out)." - }, - "gender": { - "type": "string", - "enum": [ - "male", - "female", - "other" - ], - "example": "male", - "description": "Gender of the worker (male, female, other)." - }, - "preferred_location": { - "type": "string", - "example": "Dubai Marina", - "description": "Preferred work location/area." - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - }, - "passport": { - "type": "object", - "description": "Optional JSON object containing extracted passport details.", - "properties": { - "authority": { - "type": "string" - }, - "date_of_birth": { - "type": "string", - "example": "14/05/1990" - }, - "date_of_expiry": { - "type": "string", - "example": "05/07/2022" - }, - "date_of_issue": { - "type": "string", - "example": "05/07/2017" - }, - "document_type": { - "type": "string" - }, - "given_names": { - "type": "string" - }, - "issuing_country": { - "type": "string" - }, - "nationality": { - "type": "string" - }, - "passport_number": { - "type": "string" - }, - "personal_number": { - "type": "string" - }, - "place_of_birth": { - "type": "string" - }, - "sex": { - "type": "string" - }, - "surname": { - "type": "string" - } - } - }, - "visa": { - "type": "object", - "description": "Optional JSON object containing extracted visa details.", - "properties": { - "accompanied_by": { - "type": "string" - }, - "expiry_date": { - "type": "string", - "example": "2024-02-15" - }, - "file_number": { - "type": "string" - }, - "id_number": { - "type": "string" - }, - "issue_date": { - "type": "string", - "example": "2022-02-16" - }, - "name": { - "type": "string" - }, - "passport_number": { - "type": "string" - }, - "place_of_issue": { - "type": "string" - }, - "profession": { - "type": "string" - }, - "sponsor": { - "type": "string" - } - } - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Worker registered successfully. Use the returned token for document upload steps.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Worker registered successfully. Please upload your passport and visa documents." - }, - "data": { - "type": "object", - "properties": { - "worker": { - "$ref": "#/components/schemas/Worker" - }, - "token": { - "type": "string", - "example": "abc123xyz456...secureToken..." - } - } - } - } - } - } - } - }, - "422": { - "description": "Validation error — field validation failed or Passport OCR extraction failed.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": false - }, - "message": { - "type": "string", - "example": "Validation error." - }, - "errors": { - "type": "object", - "properties": { - "phone": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "This mobile number is already registered." - ] - }, - "name": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "The name field is required." - ] - } - } - } - } - } - } - } - }, - "500": { - "description": "Server error during registration (e.g. database transaction failure).", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": false - }, - "message": { - "type": "string", - "example": "An error occurred during worker registration. Please try again." - }, - "error": { - "type": "string", - "example": "Internal Server Error" - } - } - } - } - } - } - } - } + "skills": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "Cooking" + } + } + } + } + } + } + } + } + } + } + } + }, + "/nationalities": { + "get": { + "tags": [ + "General" + ], + "summary": "Get Nationalities List", + "description": "Returns a list of supported nationalities translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", + "security": [], + "parameters": [ + { + "name": "lang", + "in": "query", + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language locale code (e.g. 'en', 'hindi', 'sawahi', 'taglo', 'tamil')" + }, + { + "name": "Accept-Language", + "in": "header", + "schema": { + "type": "string" + }, + "description": "Fallback header locale (e.g. 'en', 'hi', 'sw', 'tl', 'ta')" + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search keyword to filter nationalities by name or code" + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "Page number for pagination" + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 500 + }, + "description": "Number of nationalities to return per page. Defaults to 500 on the backend to retrieve all nationalities by default." + } + ], + "responses": { + "200": { + "description": "Nationalities list retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - "/workers/login": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Authenticate Worker", - "description": "Validates worker credentials (mobile phone number and password) and generates a secure, stateless Bearer token for api access.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone", - "password" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971509998888", - "description": "Registered mobile phone number." - }, - "password": { - "type": "string", - "format": "password", - "example": "securepassword123", - "description": "Secret account password." - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Worker logged in successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Worker logged in successfully." - }, - "data": { - "type": "object", - "properties": { - "worker": { - "$ref": "#/components/schemas/Worker" - }, - "token": { - "type": "string", - "example": "abc123xyz456...secureToken..." - } - } - } - } - } - } - } - }, - "401": { - "description": "Unauthorized access.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": false - }, - "message": { - "type": "string", - "example": "Invalid mobile number or password." - } - } - } - } - } - } - } - } - }, - "/workers/ocr/passport-vision": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Extract Passport Data using Google Cloud Vision API", - "description": "Upload a passport image file to extract OCR data using the Google Cloud Vision API.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "passport_file" - ], - "properties": { - "passport_file": { - "type": "string", - "format": "binary", - "description": "Passport image file (jpg/png/jpeg, max 10MB)." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Passport data successfully extracted.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "passport_number": { - "type": "string", - "example": "Z43R34255" - }, - "given_names": { - "type": "string", - "example": "AHMAD" - }, - "date_of_birth": { - "type": "string", - "example": "03/07/1978" - }, - "nationality": { - "type": "string", - "example": "ARE" - }, - "issuing_country": { - "type": "string", - "example": "ARE" - }, - "place_of_birth": { - "type": "string", - "example": "DUBAI" - }, - "date_of_issue": { - "type": "string", - "example": "10/02/2015" - }, - "date_of_expiry": { - "type": "string", - "example": "10/02/2020" - } - } - } - } - } - } - } - }, - "422": { - "description": "Validation error (missing passport_file)." - }, - "400": { - "description": "OCR processing error or failed Google Vision API request." - } - } - } - }, - "/workers/config": { - "get": { - "tags": [ - "Worker/Auth" - ], - "summary": "Get Supported Languages and Config", - "description": "Returns a list of supported regional languages (Hindi, Swahili, Tagalog, Tamil) for helper onboarding.", - "security": [ - - ], - "responses": { - "200": { - "description": "Config and supported languages retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "languages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "HI" - }, - "name": { - "type": "string", - "example": "Hindi (हिन्दी)" - } - } - } - } - } - } - } - } - } - } - } - }, - "/workers/skills": { - "get": { - "tags": [ - "Worker/Auth" - ], - "summary": "Get Master Skills List", - "description": "Returns a list of all available master skills for helper onboarding profile setup.", - "security": [ - - ], - "responses": { - "200": { - "description": "Master skills list retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "skills": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "name": { - "type": "string", - "example": "Cooking" - } - } - } - } - } - } - } - } - } - } - } - }, - "/nationalities": { - "get": { - "tags": [ - "General" - ], - "summary": "Get Nationalities List", - "description": "Returns a list of supported nationalities translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", - "security": [ - - ], - "parameters": [ - { - "name": "lang", - "in": "query", - "schema": { - "type": "string", - "default": "en" - }, - "description": "Language locale code (e.g. \u0027en\u0027, \u0027hindi\u0027, \u0027sawahi\u0027, \u0027taglo\u0027, \u0027tamil\u0027)" - }, - { - "name": "Accept-Language", - "in": "header", - "schema": { - "type": "string" - }, - "description": "Fallback header locale (e.g. \u0027en\u0027, \u0027hi\u0027, \u0027sw\u0027, \u0027tl\u0027, \u0027ta\u0027)" - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search keyword to filter nationalities by name or code" - }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - }, - "description": "Page number for pagination" - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 500 - }, - "description": "Number of nationalities to return per page. Defaults to 500 on the backend to retrieve all nationalities by default." - } - ], - "responses": { - "200": { - "description": "Nationalities list retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "nationalities": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "IN" - }, - "name": { - "type": "string", - "example": "Indian" - } - } - } - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 185 - }, - "per_page": { - "type": "integer", - "example": 15 - }, - "current_page": { - "type": "integer", - "example": 1 - }, - "last_page": { - "type": "integer", - "example": 13 - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/languages": { - "get": { - "tags": [ - "General" - ], - "summary": "Get Languages List", - "description": "Returns a list of supported languages translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", - "security": [ - - ], - "parameters": [ - { - "name": "lang", - "in": "query", - "schema": { - "type": "string", - "default": "en" - }, - "description": "Language locale code (e.g. \u0027en\u0027, \u0027hindi\u0027, \u0027sawahi\u0027, \u0027taglo\u0027, \u0027tamil\u0027)" - }, - { - "name": "Accept-Language", - "in": "header", - "schema": { - "type": "string" - }, - "description": "Fallback header locale (e.g. \u0027en\u0027, \u0027hi\u0027, \u0027sw\u0027, \u0027tl\u0027, \u0027ta\u0027)" - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search keyword to filter languages by name or code" - }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - }, - "description": "Page number for pagination" - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 500 - }, - "description": "Number of languages to return per page." - } - ], - "responses": { - "200": { - "description": "Languages list retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "languages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "en" - }, - "name": { - "type": "string", - "example": "English" - } - } - } - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 22 - }, - "per_page": { - "type": "integer", - "example": 15 - }, - "current_page": { - "type": "integer", - "example": 1 - }, - "last_page": { - "type": "integer", - "example": 2 - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/preferred-locations": { - "get": { - "tags": [ - "General" - ], - "summary": "Get Localized Preferred Locations List", - "description": "Returns a list of preferred locations (Dubai, Abu Dhabi, Oman) translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", - "security": [ - - ], - "parameters": [ - { - "name": "lang", - "in": "query", - "schema": { - "type": "string", - "default": "en" - }, - "description": "Language locale code (e.g. \u0027en\u0027, \u0027hindi\u0027, \u0027sawahi\u0027, \u0027taglo\u0027, \u0027tamil\u0027)" - }, - { - "name": "Accept-Language", - "in": "header", - "schema": { - "type": "string" - }, - "description": "Fallback header locale (e.g. \u0027en\u0027, \u0027hi\u0027, \u0027sw\u0027, \u0027tl\u0027, \u0027ta\u0027)" - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search keyword to filter locations by name or code" - }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - }, - "description": "Page number for pagination" - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 15 - }, - "description": "Number of locations to return per page" - } - ], - "responses": { - "200": { - "description": "Preferred locations list retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "preferred_locations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "dubai" - }, - "name": { - "type": "string", - "example": "Dubai" - } - } - } - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 3 - }, - "per_page": { - "type": "integer", - "example": 15 - }, - "current_page": { - "type": "integer", - "example": 1 - }, - "last_page": { - "type": "integer", - "example": 1 - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/preferred-locations/{location}/areas": { - "get": { - "tags": [ - "General" - ], - "summary": "Get Localized Areas for a Preferred Location (Path Parameter)", - "description": "Returns a list of localized neighborhoods or sub-locations mapped under a specific preferred location (Dubai, Abu Dhabi, Oman) using path routing.", - "security": [ - - ], - "parameters": [ - { - "name": "location", - "in": "path", - "required": true, - "description": "The preferred location name (e.g. dubai, abu dhabi, oman).", - "schema": { - "type": "string" - } - }, - { - "name": "lang", - "in": "query", - "schema": { - "type": "string", - "default": "en" - }, - "description": "Language locale code (e.g. \u0027en\u0027, \u0027hindi\u0027, \u0027sawahi\u0027, \u0027taglo\u0027, \u0027tamil\u0027)" - }, - { - "name": "Accept-Language", - "in": "header", - "schema": { - "type": "string" - }, - "description": "Fallback header locale (e.g. \u0027en\u0027, \u0027hi\u0027, \u0027sw\u0027, \u0027tl\u0027, \u0027ta\u0027)" - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search keyword to filter areas by name or code" - }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - }, - "description": "Page number for pagination" - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 15 - }, - "description": "Number of areas to return per page" - } - ], - "responses": { - "200": { - "description": "Areas retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "location": { - "type": "string", - "example": "dubai" - }, - "areas": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "marina" - }, - "name": { - "type": "string", - "example": "Dubai Marina" - } - } - } - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 17 - }, - "per_page": { - "type": "integer", - "example": 15 - }, - "current_page": { - "type": "integer", - "example": 1 - }, - "last_page": { - "type": "integer", - "example": 2 - } - } - } - } - } - } - } - } - } - } - } - } + "data": { + "type": "object", + "properties": { + "nationalities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "IN" }, - "/preferred-locations/areas": { - "get": { - "tags": [ - "General" - ], - "summary": "Get Localized Areas for a Preferred Location (Query Parameter)", - "description": "Returns a list of localized neighborhoods or sub-locations mapped under a specific preferred location (Dubai, Abu Dhabi, Oman) using query parameter routing.", - "security": [ - - ], - "parameters": [ - { - "name": "city", - "in": "query", - "required": false, - "description": "The preferred location name (e.g. dubai, abu dhabi, oman).", - "schema": { - "type": "string" - } - }, - { - "name": "location", - "in": "query", - "required": false, - "description": "Alternative key for location name (e.g. dubai, abu dhabi, oman).", - "schema": { - "type": "string" - } - }, - { - "name": "lang", - "in": "query", - "schema": { - "type": "string", - "default": "en" - }, - "description": "Language locale code (e.g. \u0027en\u0027, \u0027hindi\u0027, \u0027sawahi\u0027, \u0027taglo\u0027, \u0027tamil\u0027)" - }, - { - "name": "Accept-Language", - "in": "header", - "schema": { - "type": "string" - }, - "description": "Fallback header locale (e.g. \u0027en\u0027, \u0027hi\u0027, \u0027sw\u0027, \u0027tl\u0027, \u0027ta\u0027)" - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search keyword to filter areas by name or code" - }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - }, - "description": "Page number for pagination" - }, - { - "name": "per_page", - "in": "query", - "schema": { - "type": "integer", - "default": 15 - }, - "description": "Number of areas to return per page" - } - ], - "responses": { - "200": { - "description": "Areas retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "location": { - "type": "string", - "example": "dubai" - }, - "areas": { - "type": "array", - "items": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "marina" - }, - "name": { - "type": "string", - "example": "Dubai Marina" - } - } - } - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "example": 17 - }, - "per_page": { - "type": "integer", - "example": 15 - }, - "current_page": { - "type": "integer", - "example": 1 - }, - "last_page": { - "type": "integer", - "example": 2 - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/workers/send-otp": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Send Mobile OTP Verification Code", - "description": "Dispatches a mock 6-digit OTP verification code (\u0027111111\u0027) to the worker\u0027s mobile phone number or email address.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971501234567", - "description": "Mobile contact phone number or identifier." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OTP dispatched successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Verification code sent. (Dev mock: 111111)" - }, - "identifier": { - "type": "string", - "example": "+971501234567" - } - } - } - } - } - } - } - } - }, - "/workers/verify-otp": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Verify Mobile OTP Code", - "description": "Validates the received 6-digit OTP code against the cached record. Sets a 1-hour secure gate allowing profile setup.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone", - "otp" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971501234567" - }, - "otp": { - "type": "string", - "example": "111111", - "description": "6-digit OTP received by the worker." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OTP verified and registration gate unlocked.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "OTP verified successfully. You may proceed with profile setup." - }, - "identifier": { - "type": "string", - "example": "+971501234567" - } - } - } - } - } - }, - "422": { - "description": "Invalid OTP code or expired session." - } - } - } - }, - "/workers/setup-profile": { - "post": { - "tags": [ - "Worker/Auth" - ], - "summary": "Complete Basic Profile Setup", - "description": "Saves basic onboarding data (Name, Nationality, Language, Skills) and generates the permanent worker account along with a secure bearer access token.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone", - "name", - "nationality", - "language" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971501234567" - }, - "name": { - "type": "string", - "example": "Rahul Sharma" - }, - "nationality": { - "type": "string", - "example": "Indian" - }, - "language": { - "type": "string", - "enum": [ - "HI", - "SW", - "TL", - "TA" - ], - "example": "HI" - }, - "skills": { - "type": "array", - "items": { - "type": "integer" - }, - "example": [ - 6, - 8 - ], - "description": "Array of skill IDs" - }, - "gender": { - "type": "string", - "enum": [ - "male", - "female", - "other" - ], - "example": "male", - "description": "Gender of the worker (male, female, other)." - }, - "live_in_out": { - "type": "string", - "enum": [ - "live_in", - "live_out" - ], - "example": "live_out", - "description": "Accommodation preference (live_in, live_out)." - }, - "preferred_location": { - "type": "string", - "example": "Dubai Marina", - "description": "Preferred work location/area." - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Account created and profile configured successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Registration complete! Welcome to Migrant." - }, - "data": { - "type": "object", - "properties": { - "worker": { - "$ref": "#/components/schemas/Worker" - }, - "token": { - "type": "string", - "example": "abc123xyz456...secureToken..." - } - } - } - } - } - } - } - } - } - } - }, - "/workers/profile": { - "get": { - "tags": [ - "Worker/Profile" - ], - "summary": "Get Profile details", - "description": "Retrieves the authenticated worker\u0027s profile details including their selected job category, connected skills, and uploaded documents.", - "responses": { - "200": { - "description": "Worker profile details retrieved.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "worker": { - "$ref": "#/components/schemas/Worker" - } - } - } - } - } - } - } - } - } - } - }, - "/workers/profile/update": { - "post": { - "tags": [ - "Worker/Profile" - ], - "summary": "Update Profile details", - "description": "Allows workers to customize and complete their profiles by updating age, nationality, desired salary, bio, availability, experience, religion, categories, and master skills list.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "Amina Al-Masry" - }, - "age": { - "type": "integer", - "example": 28 - }, - "nationality": { - "type": "string", - "example": "Egypt" - }, - "salary": { - "type": "number", - "example": 1800 - }, - "availability": { - "type": "string", - "example": "Immediate" - }, - "experience": { - "type": "string", - "example": "4 Years" - }, - "religion": { - "type": "string", - "example": "Muslim" - }, - "bio": { - "type": "string", - "example": "Highly certified housekeeper and babysitter with cooking experience." - }, - "skills": { - "type": "array", - "items": { - "type": "integer" - }, - "example": [ - 6, - 8 - ] - }, - "fcm_token": { - "type": "string", - "example": "fcm-device-token-xyz-123", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Profile updated successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Profile updated successfully." - }, - "data": { - "type": "object", - "properties": { - "worker": { - "$ref": "#/components/schemas/Worker" - } - } - } - } - } - } - } - } - } - } - }, - "/workers/go-live": { - "post": { - "tags": [ - "Worker/Profile" - ], - "summary": "Profile Go Live", - "description": "Sets the worker status to \u0027active\u0027 and availability to \u0027Immediate\u0027, making the profile searchable on the sponsor/employer marketplace.", - "responses": { - "200": { - "description": "Worker profile is now live and searchable.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Your profile is now live! Status set to Active." - } - } - } - } - } - } - } - } - }, - "/workers/profile/toggle-availability": { - "post": { - "tags": [ - "Worker/Profile" - ], - "summary": "Toggle Profile Search Visibility", - "description": "Updates worker search visibility based on whether they are still actively seeking a job. \u0027still_looking\u0027 as true makes profile active, false hides it.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "still_looking" - ], - "properties": { - "still_looking": { - "type": "boolean", - "example": true, - "description": "Whether the worker is still actively looking for job offers." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Availability toggled successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "still_looking": { - "type": "boolean", - "example": true - }, - "status": { - "type": "string", - "example": "active" - } - } - } - } - } - } - } - } - }, - "/workers/profile/mark-hired": { - "post": { - "tags": [ - "Worker/Profile" - ], - "summary": "Mark Worker as Hired", - "description": "Changes the worker\u0027s status to \u0027Hired\u0027, which automatically removes them from active marketplace searches.", - "responses": { - "200": { - "description": "Worker marked as Hired.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Congratulations! You have been successfully marked as Hired." - } - } - } - } - } - } - } - } - }, - "/workers/offers": { - "get": { - "tags": [ - "Worker/Offers" - ], - "summary": "View Received Job Offers", - "description": "Returns a list of all custom job/hiring offers received by the worker from different employers (e.g. Work Date, location, salaries, and notes).", - "responses": { - "200": { - "description": "List of job offers retrieved.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "offers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JobOffer" - } - } - } - } - } - } - } - } - } - } - } - }, - "/workers/offers/{id}/respond": { - "post": { - "tags": [ - "Worker/Offers" - ], - "summary": "Accept or Reject Job Offer", - "description": "Responds to a pending job offer. If accepted, the job offer status becomes \u0027accepted\u0027 and the worker\u0027s status is automatically changed to \u0027Hired\u0027, which reflects immediately in the Employer panel.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "description": "The Job Offer ID reference.", - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "status" - ], - "properties": { - "status": { - "type": "string", - "enum": [ - "accepted", - "rejected" - ], - "example": "accepted", - "description": "Response action (accepted or rejected)." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Job offer response saved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Job offer successfully accepted." - }, - "data": { - "type": "object", - "properties": { - "offer": { - "$ref": "#/components/schemas/JobOffer" - }, - "worker_status": { - "type": "string", - "example": "Hired" - } - } - } - } - } - } - } - } - } - } - }, - "/workers/conversations": { - "get": { - "tags": [ - "Worker/Conversations" - ], - "summary": "Get Conversations List (Worker)", - "description": "Retrieves all active conversations for the authenticated worker.", - "responses": { - "200": { - "description": "Conversations list retrieved successfully." - } - } - } - }, - "/workers/conversations/{id}/messages": { - "get": { - "tags": [ - "Worker/Conversations" - ], - "summary": "Get Conversation Messages (Worker)", - "description": "Retrieves message history for a conversation and marks incoming employer messages as read.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Messages retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Worker/Conversations" - ], - "summary": "Send Reply Message (Worker)", - "description": "Sends a reply message from the worker to the employer.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "text": { - "type": "string", - "example": "Yes, I am available.", - "description": "Optional text message content (required if no file is uploaded)." - }, - "file": { - "type": "string", - "format": "binary", - "description": "Optional file or voice note attachment (Max 10MB). Supports images, documents, and common audio formats." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Message sent successfully." - } - } - } - }, - "/workers/report": { - "post": { - "tags": [ - "Worker/Profile" - ], - "summary": "Submit a Safety/Moderation Report (Worker)", - "description": "Submits a report on a chat conversation or a review. The report is logged in the safety queue and reviewed by administrators.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "type", - "item_id", - "reason" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "Chat", - "Review" - ], - "example": "Chat", - "description": "The type of entity being reported (Chat or Review)." - }, - "item_id": { - "type": "string", - "example": "1", - "description": "The ID of the conversation or review being reported." - }, - "user_id": { - "type": "string", - "example": "5", - "description": "Optional ID of the user being reported." - }, - "reason": { - "type": "string", - "example": "Others", - "description": "The reason for submitting this report." - }, - "others": { - "type": "string", - "example": "Spam and malicious links", - "description": "Custom reason string. Used only if reason is \u0027Others\u0027." - }, - "description": { - "type": "string", - "example": "The other party sent highly inappropriate and insulting messages.", - "description": "Optional detailed explanation/comments." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Report submitted successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Report submitted successfully. Our safety team will review it shortly." - }, - "report_id": { - "type": "string", - "example": "REP-ABCD1234" - } - } - } - } - } - }, - "404": { - "description": "Conversation or Review not found, or user is not authorized to report it." - }, - "422": { - "description": "Validation error." - } - } - } - }, - "/workers/report-reasons": { - "get": { - "tags": [ - "Worker/Profile" - ], - "summary": "Get Safety Report Reasons (Worker)", - "description": "Returns a list of active safety/moderation report reasons. Can filter reasons by target type (Chat or Review).", - "parameters": [ - { - "name": "type", - "in": "query", - "required": false, - "description": "Optional type to filter reasons (Chat or Review). If not provided, returns all active reasons.", - "schema": { - "type": "string", - "enum": [ - "Chat", - "Review" - ] - } - } - ], - "responses": { - "200": { - "description": "List of report reasons retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "reasons": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "reason": { - "type": "string", - "example": "Abusive language / Profanity" - }, - "type": { - "type": "string", - "example": "Both" - } - } - } - } - } - } - } - } - } - } - } - }, - "/workers/tickets": { - "get": { - "tags": [ - "Worker/Support" - ], - "summary": "List Support Tickets (Worker)", - "description": "Retrieves a paginated list of all support tickets created by the authenticated worker.", - "responses": { - "200": { - "description": "Tickets retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Worker/Support" - ], - "summary": "Create Support Ticket (Worker)", - "description": "Creates a support ticket for a worker with a subject, description, and optional priority level.", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "subject", - "description" - ], - "properties": { - "subject": { - "type": "string", - "example": "App login crashes on launch" - }, - "description": { - "type": "string", - "example": "When opening the app on my phone, it crashes immediately on the splash screen." - }, - "priority": { - "type": "string", - "enum": [ - "low", - "medium", - "high" - ], - "example": "medium" - }, - "reason_id": { - "type": "integer", - "example": 1, - "description": "Optional report/support reason ID." - }, - "voice_note": { - "type": "string", - "format": "binary", - "description": "Optional audio/voice note file attachment." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Ticket created successfully." - } - } - } - }, - "/workers/tickets/{id}": { - "get": { - "tags": [ - "Worker/Support" - ], - "summary": "Get Support Ticket Details \u0026 Replies (Worker)", - "description": "Retrieves the details of a support ticket and its chronological reply history.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Ticket details and replies retrieved successfully." - } - } + "name": { + "type": "string", + "example": "Indian" + } } + } + }, + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 185 + }, + "per_page": { + "type": "integer", + "example": 15 + }, + "current_page": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 13 + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/languages": { + "get": { + "tags": [ + "General" + ], + "summary": "Get Languages List", + "description": "Returns a list of supported languages translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", + "security": [], + "parameters": [ + { + "name": "lang", + "in": "query", + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language locale code (e.g. 'en', 'hindi', 'sawahi', 'taglo', 'tamil')" + }, + { + "name": "Accept-Language", + "in": "header", + "schema": { + "type": "string" + }, + "description": "Fallback header locale (e.g. 'en', 'hi', 'sw', 'tl', 'ta')" + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search keyword to filter languages by name or code" + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "Page number for pagination" + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 500 + }, + "description": "Number of languages to return per page." + } + ], + "responses": { + "200": { + "description": "Languages list retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "languages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "en" + }, + "name": { + "type": "string", + "example": "English" + } + } + } + }, + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 22 + }, + "per_page": { + "type": "integer", + "example": 15 + }, + "current_page": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 2 + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/preferred-locations": { + "get": { + "tags": [ + "General" + ], + "summary": "Get Localized Preferred Locations List", + "description": "Returns a list of preferred locations (Dubai, Abu Dhabi, Oman) translated to the requested language. Supports en, hindi (hi), sawahi (sw), taglo (tl), tamil (ta).", + "security": [], + "parameters": [ + { + "name": "lang", + "in": "query", + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language locale code (e.g. 'en', 'hindi', 'sawahi', 'taglo', 'tamil')" + }, + { + "name": "Accept-Language", + "in": "header", + "schema": { + "type": "string" + }, + "description": "Fallback header locale (e.g. 'en', 'hi', 'sw', 'tl', 'ta')" + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search keyword to filter locations by name or code" + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "Page number for pagination" + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 15 + }, + "description": "Number of locations to return per page" + } + ], + "responses": { + "200": { + "description": "Preferred locations list retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "preferred_locations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "dubai" + }, + "name": { + "type": "string", + "example": "Dubai" + } + } + } + }, + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 3 + }, + "per_page": { + "type": "integer", + "example": 15 + }, + "current_page": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 1 + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/preferred-locations/{location}/areas": { + "get": { + "tags": [ + "General" + ], + "summary": "Get Localized Areas for a Preferred Location (Path Parameter)", + "description": "Returns a list of localized neighborhoods or sub-locations mapped under a specific preferred location (Dubai, Abu Dhabi, Oman) using path routing.", + "security": [], + "parameters": [ + { + "name": "location", + "in": "path", + "required": true, + "description": "The preferred location name (e.g. dubai, abu dhabi, oman).", + "schema": { + "type": "string" + } + }, + { + "name": "lang", + "in": "query", + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language locale code (e.g. 'en', 'hindi', 'sawahi', 'taglo', 'tamil')" + }, + { + "name": "Accept-Language", + "in": "header", + "schema": { + "type": "string" + }, + "description": "Fallback header locale (e.g. 'en', 'hi', 'sw', 'tl', 'ta')" + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search keyword to filter areas by name or code" + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "Page number for pagination" + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 15 + }, + "description": "Number of areas to return per page" + } + ], + "responses": { + "200": { + "description": "Areas retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "location": { + "type": "string", + "example": "dubai" + }, + "areas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "marina" + }, + "name": { + "type": "string", + "example": "Dubai Marina" + } + } + } + }, + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 17 + }, + "per_page": { + "type": "integer", + "example": 15 + }, + "current_page": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 2 + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/preferred-locations/areas": { + "get": { + "tags": [ + "General" + ], + "summary": "Get Localized Areas for a Preferred Location (Query Parameter)", + "description": "Returns a list of localized neighborhoods or sub-locations mapped under a specific preferred location (Dubai, Abu Dhabi, Oman) using query parameter routing.", + "security": [], + "parameters": [ + { + "name": "city", + "in": "query", + "required": false, + "description": "The preferred location name (e.g. dubai, abu dhabi, oman).", + "schema": { + "type": "string" + } + }, + { + "name": "location", + "in": "query", + "required": false, + "description": "Alternative key for location name (e.g. dubai, abu dhabi, oman).", + "schema": { + "type": "string" + } + }, + { + "name": "lang", + "in": "query", + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language locale code (e.g. 'en', 'hindi', 'sawahi', 'taglo', 'tamil')" + }, + { + "name": "Accept-Language", + "in": "header", + "schema": { + "type": "string" + }, + "description": "Fallback header locale (e.g. 'en', 'hi', 'sw', 'tl', 'ta')" + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search keyword to filter areas by name or code" + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "Page number for pagination" + }, + { + "name": "per_page", + "in": "query", + "schema": { + "type": "integer", + "default": 15 + }, + "description": "Number of areas to return per page" + } + ], + "responses": { + "200": { + "description": "Areas retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "location": { + "type": "string", + "example": "dubai" + }, + "areas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "marina" + }, + "name": { + "type": "string", + "example": "Dubai Marina" + } + } + } + }, + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 17 + }, + "per_page": { + "type": "integer", + "example": 15 + }, + "current_page": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 2 + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/workers/send-otp": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Send Mobile OTP Verification Code", + "description": "Dispatches a mock 6-digit OTP verification code ('111111') to the worker's mobile phone number or email address.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971501234567", + "description": "Mobile contact phone number or identifier." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OTP dispatched successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Verification code sent. (Dev mock: 111111)" + }, + "identifier": { + "type": "string", + "example": "+971501234567" + } + } + } + } + } + } + } + } + }, + "/workers/verify-otp": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Verify Mobile OTP Code", + "description": "Validates the received 6-digit OTP code against the cached record. Sets a 1-hour secure gate allowing profile setup.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone", + "otp" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971501234567" + }, + "otp": { + "type": "string", + "example": "111111", + "description": "6-digit OTP received by the worker." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OTP verified and registration gate unlocked.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "OTP verified successfully. You may proceed with profile setup." + }, + "identifier": { + "type": "string", + "example": "+971501234567" + } + } + } + } + } + }, + "422": { + "description": "Invalid OTP code or expired session." + } + } + } + }, + "/workers/setup-profile": { + "post": { + "tags": [ + "Worker/Auth" + ], + "summary": "Complete Basic Profile Setup", + "description": "Saves basic onboarding data (Name, Nationality, Language, Skills) and generates the permanent worker account along with a secure bearer access token.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone", + "name", + "nationality", + "language" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971501234567" + }, + "name": { + "type": "string", + "example": "Rahul Sharma" + }, + "nationality": { + "type": "string", + "example": "Indian" + }, + "language": { + "type": "string", + "enum": [ + "HI", + "SW", + "TL", + "TA" + ], + "example": "HI" + }, + "skills": { + "type": "array", + "items": { + "type": "integer" + }, + "example": [ + 6, + 8 + ], + "description": "Array of skill IDs" + }, + "gender": { + "type": "string", + "enum": [ + "male", + "female", + "other" + ], + "example": "male", + "description": "Gender of the worker (male, female, other)." + }, + "live_in_out": { + "type": "string", + "enum": [ + "live_in", + "live_out" + ], + "example": "live_out", + "description": "Accommodation preference (live_in, live_out)." + }, + "preferred_location": { + "type": "string", + "example": "Dubai Marina", + "description": "Preferred work location/area." + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Account created and profile configured successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Registration complete! Welcome to Migrant." + }, + "data": { + "type": "object", + "properties": { + "worker": { + "$ref": "#/components/schemas/Worker" + }, + "token": { + "type": "string", + "example": "abc123xyz456...secureToken..." + } + } + } + } + } + } + } + } + } + } + }, + "/workers/profile": { + "get": { + "tags": [ + "Worker/Profile" + ], + "summary": "Get Profile details", + "description": "Retrieves the authenticated worker's profile details including their selected job category, connected skills, and uploaded documents.", + "responses": { + "200": { + "description": "Worker profile details retrieved.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "worker": { + "$ref": "#/components/schemas/Worker" + } + } + } + } + } + } + } + } + } + } + }, + "/workers/profile/update": { + "post": { + "tags": [ + "Worker/Profile" + ], + "summary": "Update Profile details", + "description": "Allows workers to customize and complete their profiles by updating name, phone, age, nationality, desired salary, language, preferred job type, live in/out accommodation preference, preferred location, experience, skills list, fcm_token, passport details, and visa details.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Amina Al-Masry" + }, + "phone": { + "type": "string", + "example": "+971509998888" + }, + "age": { + "type": "integer", + "example": 28 + }, + "nationality": { + "type": "string", + "example": "Egypt" + }, + "language": { + "type": "string", + "enum": [ + "HI", + "SW", + "TL", + "TA" + ], + "example": "HI" + }, + "salary": { + "type": "number", + "example": 1800 + }, + "preferred_job_type": { + "type": "string", + "example": "full-time", + "description": "Preferred job type (e.g., full-time, part-time)." + }, + "live_in_out": { + "type": "string", + "enum": [ + "live_in", + "live_out" + ], + "example": "live_out", + "description": "Accommodation preference (live_in, live_out)." + }, + "preferred_location": { + "type": "string", + "example": "Dubai Marina", + "description": "Preferred work location/area." + }, + "experience": { + "type": "string", + "example": "4 Years" + }, + "skills": { + "type": "array", + "items": { + "type": "integer" + }, + "example": [ + 6, + 8 + ] + }, + "fcm_token": { + "type": "string", + "example": "fcm-device-token-xyz-123", + "description": "Firebase Cloud Messaging token for push notifications." + }, + "passport": { + "type": "object", + "properties": { + "passport_number": { + "type": "string", + "example": "ABC123456" }, - "/workers/tickets/{id}/reply": { - "post": { - "tags": [ - "Worker/Support" - ], - "summary": "Reply to Support Ticket (Worker)", - "description": "Submits a text reply or message to an active, open, or resolved support ticket.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "I tried clearing cache and it still crashes." - }, - "voice_note": { - "type": "string", - "format": "binary", - "description": "Optional audio/voice note file attachment." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Reply posted successfully." - } - } - } - }, - "/employers/register": { - "post": { - "tags": [ - "Employer/Auth" - ], - "summary": "Register Sponsor (Alias)", - "description": "Legacy route matching the simplified sponsor registration (Name, Email, Phone) (Step 1).", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "name", - "email", - "phone" - ], - "properties": { - "name": { - "type": "string", - "example": "Ahmad Bin Ahmed", - "description": "Sponsor\u0027s full name" - }, - "email": { - "type": "string", - "example": "ahmad@example.com", - "description": "Sponsor\u0027s contact email address" - }, - "phone": { - "type": "string", - "example": "+971509990001", - "description": "Sponsor\u0027s mobile phone number" - }, - "emirates_id": { - "type": "object", - "description": "Optional JSON object containing extracted Emirates ID details.", - "properties": { - "emirates_id_number": { - "type": "string", - "example": "784-1988-5310327-2" - }, - "name": { - "type": "string", - "example": "Ahmad Bin Ahmed" - }, - "date_of_birth": { - "type": "string", - "example": "1990-01-01" - }, - "nationality": { - "type": "string", - "example": "Emirati" - }, - "issue_date": { - "type": "string", - "example": "2023-04-11" - }, - "expiry_date": { - "type": "string", - "example": "2028-04-11" - }, - "employer": { - "type": "string", - "example": "Federal Authority" - }, - "issue_place": { - "type": "string", - "example": "Abu Dhabi" - }, - "occupation": { - "type": "string", - "example": "Manager" - } - } - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - }, - "address": { - "type": "string", - "example": "Villa 45, Street 12", - "description": "Physical address/location details." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Sponsor registered successfully." - } - } - } - }, - "/employers/verify": { - "post": { - "tags": [ - "Employer/Auth" - ], - "summary": "Verify Sponsor Email OTP", - "description": "Verifies the email address with the OTP verification code (Step 2).", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email", - "otp" - ], - "properties": { - "email": { - "type": "string", - "example": "ahmad@example.com" - }, - "otp": { - "type": "string", - "example": "111111", - "description": "6-digit OTP code" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Email verified successfully." - } - } - } - }, - "/employers/payment": { - "post": { - "tags": [ - "Employer/Auth" - ], - "summary": "Sponsor Subscription Payment", - "description": "Submits a successful plan selection and PayTabs payment confirmation (Step 4).", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email", - "plan_id", - "amount_aed", - "paytabs_transaction_id" - ], - "properties": { - "email": { - "type": "string", - "example": "ahmad@example.com" - }, - "plan_id": { - "type": "string", - "example": "premium" - }, - "amount_aed": { - "type": "number", - "example": 199 - }, - "paytabs_transaction_id": { - "type": "string", - "example": "TXN998877" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Subscription payment confirmed." - } - } - } - }, - "/employers/password": { - "post": { - "tags": [ - "Employer/Auth" - ], - "summary": "Create Sponsor Account Password", - "description": "Configures and finalizes the portal login password to complete registration (Step 5). Returns bearer token.", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email", - "password", - "password_confirmation" - ], - "properties": { - "email": { - "type": "string", - "example": "ahmad@example.com" - }, - "password": { - "type": "string", - "format": "password", - "example": "Password@123" - }, - "password_confirmation": { - "type": "string", - "format": "password", - "example": "Password@123" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password set and registration finalized successfully." - } - } - } - }, - "/employers/plans": { - "get": { - "tags": [ - "Employer/Auth" - ], - "summary": "Get Available Subscription Plans", - "description": "Returns details (price, features) of subscription packages available to sponsors.", - "security": [ - - ], - "responses": { - "200": { - "description": "Subscription plans list retrieved successfully." - } - } - } - }, - "/employers/login": { - "post": { - "tags": [ - "Employer/Auth" - ], - "summary": "Authenticate Employer or Sponsor", - "description": "Unified authentication endpoint. Validates email/mobile and password, then returns a stateless Bearer token and the user\u0027s role (employer or sponsor).", - "security": [ - - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "password" - ], - "properties": { - "email": { - "type": "string", - "example": "ahmad@example.com", - "description": "Email address (required if mobile is not provided)" - }, - "mobile": { - "type": "string", - "example": "+971509990001", - "description": "Mobile number (required if email is not provided)" - }, - "password": { - "type": "string", - "example": "Password@123" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Login successful. Returns Bearer token and role.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Login successful." - }, - "role": { - "type": "string", - "example": "employer", - "enum": [ - "employer", - "sponsor" - ] - }, - "data": { - "type": "object", - "properties": { - "token": { - "type": "string", - "example": "api_token_here" - } - } - } - } - } - } - } - } - } - } - }, - "/employers/conversations": { - "get": { - "tags": [ - "Employer/Conversations" - ], - "summary": "Get Conversations List (Employer)", - "description": "Retrieves all active candidate conversations for the authenticated employer.", - "responses": { - "200": { - "description": "Conversations list retrieved successfully." - } - } - } - }, - "/employers/conversations/{id}/messages": { - "get": { - "tags": [ - "Employer/Conversations" - ], - "summary": "Get Conversation Messages (Employer)", - "description": "Retrieves message history for a conversation and marks incoming worker messages as read.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Messages retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Employer/Conversations" - ], - "summary": "Send Reply Message (Employer)", - "description": "Sends a reply message from the employer to the worker.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "text": { - "type": "string", - "example": "When can you start?", - "description": "Optional text message content (required if no file is uploaded)." - }, - "file": { - "type": "string", - "format": "binary", - "description": "Optional file or voice note attachment (Max 10MB). Supports images, documents, and common audio formats." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Message sent successfully." - } - } - } - }, - "/employers/conversations/start": { - "post": { - "tags": [ - "Employer/Conversations" - ], - "summary": "Start Conversation with Worker", - "description": "Starts or retrieves a conversation with the specified worker ID.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "worker_id" - ], - "properties": { - "worker_id": { - "type": "integer", - "example": 1 - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Conversation started successfully." - } - } - } - }, - "/workers/announcements": { - "get": { - "tags": [ - "Worker/CharityEvents" - ], - "summary": "Get Charity Events (Worker)", - "description": "Allows workers to retrieve the list of active charity events, food drives, and medical support programs scheduled in Dubai.", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "List of charity events retrieved successfully." - } - } - } - }, - "/workers/announcements/new": { - "get": { - "tags": [ - "Worker/CharityEvents" - ], - "summary": "Get New/Unviewed Announcements/Charity Events", - "description": "Retrieves up to 3 active approved charity events/announcements that the worker has not yet viewed, and automatically marks them as viewed.", - "responses": { - "200": { - "description": "Successfully retrieved new announcements.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 4 - }, - "title": { - "type": "string", - "example": "Free Dental Checkup Camp" - }, - "body": { - "type": "string", - "example": "Emirates Charity is providing free screening." - }, - "type": { - "type": "string", - "example": "charity" - }, - "employer_name": { - "type": "string", - "example": "Emirates Charity" - }, - "company_name": { - "type": "string", - "example": "Emirates Charity Foundation" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2026-06-01T10:00:00.000000Z" - }, - "time_ago": { - "type": "string", - "example": "5 hours ago" - } - } - } - } - } - } - } - } - }, - "401": { - "description": "Unauthenticated." - } - } - } - }, - "/workers/announcements/{id}/view": { - "post": { - "tags": [ - "Worker/CharityEvents" - ], - "summary": "Mark Specific Announcement as Viewed", - "description": "Explicitly marks a specific announcement/charity event as viewed by the authenticated worker.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "description": "Announcement ID", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Announcement marked as viewed successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Announcement marked as viewed." - } - } - } - } - } - }, - "401": { - "description": "Unauthenticated." - }, - "404": { - "description": "Announcement not found." - } - } - } - }, - "/employers/announcements": { - "get": { - "tags": [ - "Employer/CharityEvents" - ], - "summary": "Get Posted Charity Events (Employer)", - "description": "Allows employers/sponsors to retrieve the list of charity events they have published.", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "List of posted charity events retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Employer/CharityEvents" - ], - "summary": "Publish Charity Event (Employer)", - "description": "Allows employers/sponsors to publish a new community charity event or drive to all workers in Dubai, triggering instant push notifications and morning-of reminders.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "title", - "content", - "provided_items", - "event_date", - "event_time", - "location_details", - "location_pin" - ], - "properties": { - "title": { - "type": "string", - "example": "Free Dental Checkup Camp" - }, - "content": { - "type": "string", - "example": "Emirates Charity is providing free professional dental screening, cleanings, and educational packages for domestic helpers." - }, - "provided_items": { - "type": "string", - "example": "Free Dental screening, cleanings, and wellness kits" - }, - "event_date": { - "type": "string", - "format": "date", - "example": "2026-06-15" - }, - "event_time": { - "type": "string", - "example": "9:00 AM - 4:00 PM" - }, - "location_details": { - "type": "string", - "example": "Al Quoz Community Hall, Dubai" - }, - "location_pin": { - "type": "string", - "format": "uri", - "example": "https://maps.app.goo.gl/xyz" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Charity event created and notifications scheduled successfully." - } - } - } - }, - "/employers/announcements/{id}": { - "delete": { - "tags": [ - "Employer/CharityEvents" - ], - "summary": "Delete Charity Event (Employer)", - "description": "Allows employers/sponsors to delete a previously published charity event.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Charity event deleted successfully." - } - } - } - }, - "/employers/dashboard": { - "get": { - "tags": [ - "Employer/Profile" - ], - "summary": "Get Employer Dashboard stats \u0026 recent events", - "description": "Retrieves stats including contacted workers count, total hired workers, saved candidates, current plan details, and recent announcements or events.", - "responses": { - "200": { - "description": "Employer dashboard stats retrieved successfully." - } - } - } - }, - "/employers/profile": { - "get": { - "tags": [ - "Employer/Profile" - ], - "summary": "Get Profile details (Employer)", - "description": "Retrieves the authenticated employer\u0027s profile details including their selected company name, phone, language preference, and notification settings.", - "responses": { - "200": { - "description": "Employer profile details retrieved successfully." - } - } - } - }, - "/employers/profile/update": { - "post": { - "tags": [ - "Employer/Profile" - ], - "summary": "Update Profile details (Employer)", - "description": "Allows employers to update their profile details (company name, phone number, name, email, language preference, and notification settings) and change their account password.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "name", - "email", - "phone", - "company_name", - "language", - "notifications" - ], - "properties": { - "name": { - "type": "string", - "example": "Ahmad" - }, - "email": { - "type": "string", - "example": "ahmad@example.com" - }, - "phone": { - "type": "string", - "example": "+971509990001" - }, - "company_name": { - "type": "string", - "example": "Ahmad Tech Ltd" - }, - "language": { - "type": "string", - "enum": [ - "English", - "Arabic", - "english", - "arabic" - ], - "example": "English" - }, - "notifications": { - "type": "boolean", - "example": true - }, - "current_password": { - "type": "string", - "example": "Password@123" - }, - "new_password": { - "type": "string", - "example": "NewSecurePassword@123" - }, - "new_password_confirmation": { - "type": "string", - "example": "NewSecurePassword@123" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example", - "description": "Firebase Cloud Messaging token for push notifications." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Profile updated successfully." - } - } - } - }, - "/employers/workers": { - "get": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Get Available Workers", - "description": "Returns a list of verified workers that are currently available to be hired.", - "parameters": [ - { - "name": "search", - "in": "query", - "required": false, - "description": "Search term matching helper name, nationality, or religion.", - "schema": { - "type": "string" - } - }, - { - "name": "skills", - "in": "query", - "required": false, - "description": "Filter by skills (comma-separated list).", - "schema": { - "type": "string" - } - }, - { - "name": "language", - "in": "query", - "required": false, - "description": "Filter by language.", - "schema": { - "type": "string" - } - }, - { - "name": "nationality", - "in": "query", - "required": false, - "description": "Filter by nationality name (comma-separated list for multiple choice, e.g. Indian,Kenyan,Ethiopian).", - "schema": { - "type": "string" - } - }, - { - "name": "gender", - "in": "query", - "required": false, - "description": "Filter by gender (e.g. male, female).", - "schema": { - "type": "string" - } - }, - { - "name": "preferred_location", - "in": "query", - "required": false, - "description": "Filter by preferred location (comma-separated list, e.g. Dubai,Abu Dhabi).", - "schema": { - "type": "string" - } - }, - { - "name": "area", - "in": "query", - "required": false, - "description": "Filter by preferred area (comma-separated list, e.g. Marina,Al Barsha).", - "schema": { - "type": "string" - } - }, - { - "name": "job_type", - "in": "query", - "required": false, - "description": "Filter by job type (comma-separated list, e.g. full-time,part-time).", - "schema": { - "type": "string" - } - }, - { - "name": "accommodation_type", - "in": "query", - "required": false, - "description": "Filter by accommodation type (comma-separated list, e.g. live_in,live_out).", - "schema": { - "type": "string" - } - }, - { - "name": "in_country", - "in": "query", - "required": false, - "description": "Filter by in country status (true/false or yes/no or in/out).", - "schema": { - "type": "string" - } - }, - { - "name": "out_country", - "in": "query", - "required": false, - "description": "Filter by out country status (true/false or yes/no or out/in).", - "schema": { - "type": "string" - } - }, - { - "name": "visa_status", - "in": "query", - "required": false, - "description": "Filter by next visa status (comma-separated list, e.g. Residence Visa,Tourist Visa). Only applicable if in country.", - "schema": { - "type": "string" - } - }, - { - "name": "availability", - "in": "query", - "required": false, - "description": "Filter by availability status.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "Available workers list retrieved successfully." - } - } - } - }, - "/employers/workers/{id}": { - "get": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Get Worker Profile Detail", - "description": "Retrieves the complete profile details for a worker, mirroring the web detailed view, along with active conversation details if already started with this employer.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "description": "Worker ID reference.", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Worker profile details retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "worker": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "nationality": { - "type": "string", - "example": "Filipino" - }, - "photo": { - "type": "string", - "example": "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format\u0026fit=crop\u0026q=80\u0026w=200" - }, - "emirates_id_status": { - "type": "string", - "example": "Passport Verified", - "description": "Deprecated - Use passport_status instead" - }, - "passport_status": { - "type": "string", - "example": "Passport Verified" - }, - "category": { - "type": "string", - "example": "Domestic Worker" - }, - "skills": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "childcare", - "cooking" - ] - }, - "availability_status": { - "type": "string", - "example": "Active" - }, - "visa_status": { - "type": "string", - "example": "Residence Visa" - }, - "experience": { - "type": "string", - "example": "5 Years" - }, - "experience_years": { - "type": "integer", - "example": 5 - }, - "salary": { - "type": "integer", - "example": 2500 - }, - "religion": { - "type": "string", - "example": "Christian" - }, - "languages": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "English", - "Tagalog" - ] - }, - "age": { - "type": "integer", - "example": 28 - }, - "verified": { - "type": "boolean", - "example": true - }, - "preferred_job_type": { - "type": "string", - "example": "live-in" - }, - "bio": { - "type": "string", - "example": "Experienced and caring domestic worker specialing in childcare and housekeeping..." - }, - "rating": { - "type": "number", - "example": 4.3 - }, - "reviews_count": { - "type": "integer", - "example": 6 - }, - "reviews": { - "type": "array", - "items": { - "type": "object" - } - }, - "similar_workers": { - "type": "array", - "items": { - "type": "object" - } - }, - "conversation_id": { - "type": "integer", - "nullable": true, - "example": 4 - } - } - } - } - } - } - } - } - } - }, - "404": { - "description": "Worker profile not found." - } - } - } - }, - "/employers/candidates": { - "get": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Get Candidate Applications and Offers", - "description": "Returns all job applications and direct hiring offers connected to the authenticated sponsor account that have achieved \u0027Hired\u0027 status.", - "parameters": [ - { - "name": "search", - "in": "query", - "required": false, - "description": "Search term matching helper name, nationality, or religion.", - "schema": { - "type": "string" - } - }, - { - "name": "skills", - "in": "query", - "required": false, - "description": "Filter candidates by skills (comma-separated list of skill names, e.g. cooking,driving).", - "schema": { - "type": "string" - } - }, - { - "name": "languages", - "in": "query", - "required": false, - "description": "Filter candidates by languages spoken (comma-separated list of language names, e.g. English,Arabic).", - "schema": { - "type": "string" - } - }, - { - "name": "nationality", - "in": "query", - "required": false, - "description": "Filter candidates by nationality name (comma-separated list for multiple choice, e.g. Indian,Kenyan,Ethiopian).", - "schema": { - "type": "string" - } - }, - { - "name": "gender", - "in": "query", - "required": false, - "description": "Filter candidates by gender (e.g. male, female).", - "schema": { - "type": "string" - } - }, - { - "name": "preferred_location", - "in": "query", - "required": false, - "description": "Filter candidates by preferred location (comma-separated list, e.g. Dubai,Abu Dhabi).", - "schema": { - "type": "string" - } - }, - { - "name": "job_type", - "in": "query", - "required": false, - "description": "Filter candidates by job type (comma-separated list, e.g. full-time,part-time).", - "schema": { - "type": "string" - } - }, - { - "name": "accommodation_type", - "in": "query", - "required": false, - "description": "Filter candidates by accommodation type (comma-separated list, e.g. live_in,live_out).", - "schema": { - "type": "string" - } - }, - { - "name": "in_country", - "in": "query", - "required": false, - "description": "Filter candidates by in country status (true/false or yes/no or in/out).", - "schema": { - "type": "string" - } - }, - { - "name": "out_country", - "in": "query", - "required": false, - "description": "Filter candidates by out country status (true/false or yes/no or out/in).", - "schema": { - "type": "string" - } - }, - { - "name": "visa_status", - "in": "query", - "required": false, - "description": "Filter candidates by next visa status (comma-separated list, e.g. Residence Visa,Tourist Visa). Only applicable if in country.", - "schema": { - "type": "string" - } - }, - { - "name": "availability", - "in": "query", - "required": false, - "description": "Filter candidates by availability status.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "Employer candidate pipeline retrieved successfully." - } - } - } + "date_of_expiry": { + "type": "string", + "example": "2030-01-01" }, - "/employers/candidates/{id}/hire": { - "post": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Mark Candidate as Hired", - "description": "Transitions the target candidate application, job offer, or helper status to \u0027Hired\u0027.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "description": "The target identifier (Application ID, Offer ID, or Worker ID).", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Candidate successfully status updated to Hired." - } - } - } - }, - "/employers/shortlist": { - "get": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Get Shortlisted Workers", - "description": "Returns a list of all workers shortlisted by the authenticated sponsor.", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "Shortlisted workers retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Employer/Pipeline" - ], - "summary": "Toggle Worker Shortlist Status", - "description": "Toggles (adds or removes) a worker to/from the authenticated sponsor\u0027s shortlist.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "worker_id" - ], - "properties": { - "worker_id": { - "type": "integer", - "example": 1, - "description": "Worker ID to shortlist/unshortlist." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Shortlist status toggled successfully." - } - } - } - }, - "/employers/payments": { - "get": { - "tags": [ - "Employer/Billing" - ], - "summary": "Get Payment History", - "description": "Retrieves the complete transaction history for the authenticated sponsor/employer, including subscription passes and document verification receipts.", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "Employer billing history retrieved successfully." - } - } - } - }, - "/workers/dashboard/views": { - "get": { - "tags": [ - "Worker/Dashboard" - ], - "summary": "Get Worker Profile Views Statistics", - "description": "Allows authenticated workers to see the total count and detailed list of employers/sponsors who viewed their full profile.", - "responses": { - "200": { - "description": "Profile views statistics retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "views_count": { - "type": "integer", - "example": 5 - }, - "views_list": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "employer_id": { - "type": "integer", - "example": 2 - }, - "employer_name": { - "type": "string", - "example": "Ahmad" - }, - "company_name": { - "type": "string", - "example": "Ahmad Tech Ltd" - }, - "nationality": { - "type": "string", - "example": "UAE" - }, - "city": { - "type": "string", - "example": "Dubai" - }, - "viewed_at": { - "type": "string", - "format": "date-time", - "example": "2026-06-01T15:10:24.000000Z" - }, - "viewed_at_formatted": { - "type": "string", - "example": "2 minutes ago" - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/workers/dashboard": { - "get": { - "tags": [ - "Worker/Dashboard" - ], - "summary": "Get Worker Consolidated Dashboard Data", - "description": "Allows authenticated workers to retrieve their dashboard metrics: active status, profile views count, currently working sponsor/employer details, and latest charity events/announcements.", - "responses": { - "200": { - "description": "Worker dashboard details retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": { - "type": "object", - "properties": { - "active_status": { - "type": "string", - "example": "active" - }, - "count_sponsors_viewed": { - "type": "integer", - "example": 5 - }, - "employer_contacted": { - "type": "integer", - "example": 2, - "description": "Unique count of employers who contacted this worker." - }, - "profile_viewed": { - "type": "integer", - "example": 3, - "description": "Unique count of employers who viewed this worker\u0027s profile." - }, - "currently_working_sponsor": { - "type": "object", - "nullable": true, - "properties": { - "id": { - "type": "integer", - "example": 2 - }, - "name": { - "type": "string", - "example": "Ahmad" - }, - "company_name": { - "type": "string", - "example": "Ahmad Tech Ltd" - }, - "nationality": { - "type": "string", - "example": "UAE" - }, - "city": { - "type": "string", - "example": "Dubai" - }, - "email": { - "type": "string", - "example": "ahmad@example.com" - }, - "phone": { - "type": "string", - "example": "+971509990001" - }, - "hired_at": { - "type": "string", - "format": "date-time", - "example": "2026-06-01T15:10:24.000000Z" - }, - "hired_at_formatted": { - "type": "string", - "example": "Jun 01, 2026" - } - } - }, - "latest_charity_events_list": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "title": { - "type": "string", - "example": "Free Dental Checkup Camp" - }, - "body": { - "type": "string", - "example": "Emirates Charity is providing free screening." - }, - "type": { - "type": "string", - "example": "charity" - }, - "employer_name": { - "type": "string", - "example": "Emirates Charity" - }, - "company_name": { - "type": "string", - "example": "Emirates Charity Foundation" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2026-06-01T10:00:00.000000Z" - }, - "time_ago": { - "type": "string", - "example": "5 hours ago" - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "/employers/reviews": { - "get": { - "tags": [ - "Employer/Reviews" - ], - "summary": "Get Reviews Written by Employer", - "description": "Retrieves a paginated list of all reviews written by the authenticated employer.", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "description": "Page number.", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "per_page", - "in": "query", - "required": false, - "description": "Number of items per page.", - "schema": { - "type": "integer", - "default": 15 - } - } - ], - "responses": { - "200": { - "description": "Reviews retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Employer/Reviews" - ], - "summary": "Add or Update Worker Review", - "description": "Allows employers to submit a review score and description comment for a worker. If the employer has already left a review, this will update it.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "worker_id", - "rating" - ], - "properties": { - "worker_id": { - "type": "integer", - "example": 136, - "description": "Worker ID being reviewed." - }, - "rating": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "example": 5, - "description": "Review score from 1 to 5." - }, - "comment": { - "type": "string", - "example": "Highly professional, punctual and did a great job!", - "description": "Optional detailed comment." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Review updated successfully." - }, - "201": { - "description": "Review added successfully." - }, - "422": { - "description": "Validation error." - } - } - } - }, - "/employers/reviews/{id}": { - "put": { - "tags": [ - "Employer/Reviews" - ], - "summary": "Edit Worker Review", - "description": "Allows employers to edit their previously created review score and comment by review ID.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "description": "Review ID reference.", - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "rating" - ], - "properties": { - "rating": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "example": 4, - "description": "Updated rating score from 1 to 5." - }, - "comment": { - "type": "string", - "example": "Updated feedback comment.", - "description": "Updated review comment description." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Review edited successfully." - }, - "404": { - "description": "Review not found or unauthorized." - }, - "422": { - "description": "Validation error." - } - } - } - }, - "/employers/report": { - "post": { - "tags": [ - "Employer/Reviews" - ], - "summary": "Submit a Safety/Moderation Report (Employer)", - "description": "Submits a report on a chat conversation or a review. The report is logged in the safety queue and reviewed by administrators.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "type", - "item_id", - "reason" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "Chat", - "Review" - ], - "example": "Chat", - "description": "The type of entity being reported (Chat or Review)." - }, - "item_id": { - "type": "string", - "example": "1", - "description": "The ID of the conversation or review being reported." - }, - "user_id": { - "type": "string", - "example": "5", - "description": "Optional ID of the user being reported." - }, - "reason": { - "type": "string", - "example": "Others", - "description": "The reason for submitting this report." - }, - "others": { - "type": "string", - "example": "Spam and malicious links", - "description": "Custom reason string. Used only if reason is \u0027Others\u0027." - }, - "description": { - "type": "string", - "example": "The worker requested payments off-platform and sent abusive messages.", - "description": "Optional detailed explanation/comments." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Report submitted successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "Report submitted successfully. Our safety team will review it shortly." - }, - "report_id": { - "type": "string", - "example": "REP-ABCD1234" - } - } - } - } - } - }, - "404": { - "description": "Conversation or Review not found, or user is not authorized to report it." - }, - "422": { - "description": "Validation error." - } - } - } + "date_of_issue": { + "type": "string", + "example": "2020-01-01" + } + } + }, + "visa": { + "type": "object", + "properties": { + "file_number": { + "type": "string", + "example": "202/2022/2840234" + }, + "expiry_date": { + "type": "string", + "example": "2028-12-31" + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Profile updated successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - "/employers/report-reasons": { - "get": { - "tags": [ - "Employer/Reviews" - ], - "summary": "Get Safety Report Reasons (Employer)", - "description": "Returns a list of active safety/moderation report reasons. Can filter reasons by target type (Chat or Review).", - "parameters": [ - { - "name": "type", - "in": "query", - "required": false, - "description": "Optional type to filter reasons (Chat or Review). If not provided, returns all active reasons.", - "schema": { - "type": "string", - "enum": [ - "Chat", - "Review" - ] - } - } - ], - "responses": { - "200": { - "description": "List of report reasons retrieved successfully.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "reasons": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "reason": { - "type": "string", - "example": "Abusive language / Profanity" - }, - "type": { - "type": "string", - "example": "Both" - } - } - } - } - } - } - } - } - } - } - } - }, - "/employers/tickets": { - "get": { - "tags": [ - "Employer/Support" - ], - "summary": "List Support Tickets (Employer)", - "description": "Retrieves a paginated list of all support tickets created by the authenticated employer.", - "responses": { - "200": { - "description": "Tickets retrieved successfully." - } - } - }, - "post": { - "tags": [ - "Employer/Support" - ], - "summary": "Create Support Ticket (Employer)", - "description": "Creates a support ticket for an employer with a subject, description, and optional priority level.", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "subject", - "description" - ], - "properties": { - "subject": { - "type": "string", - "example": "Payment card rejected" - }, - "description": { - "type": "string", - "example": "My Visa card was declined when trying to buy the Premium Employer Pass." - }, - "priority": { - "type": "string", - "enum": [ - "low", - "medium", - "high" - ], - "example": "medium" - }, - "reason_id": { - "type": "integer", - "example": 1, - "description": "Optional report/support reason ID." - }, - "voice_note": { - "type": "string", - "format": "binary", - "description": "Optional audio/voice note file attachment." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Ticket created successfully." - } - } - } - }, - "/employers/tickets/{id}": { - "get": { - "tags": [ - "Employer/Support" - ], - "summary": "Get Support Ticket Details \u0026 Replies (Employer)", - "description": "Retrieves the details of a support ticket and its chronological reply history.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Ticket details and replies retrieved successfully." - } - } - } - }, - "/employers/tickets/{id}/reply": { - "post": { - "tags": [ - "Employer/Support" - ], - "summary": "Reply to Support Ticket (Employer)", - "description": "Submits a text reply or message to an active, open, or resolved support ticket.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "I tried a different card and it still fails." - }, - "voice_note": { - "type": "string", - "format": "binary", - "description": "Optional audio/voice note file attachment." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Reply posted successfully." - } - } - } - }, - "/workers/forgot-password": { - "post": { - "tags": [ - "Workers - Auth" - ], - "summary": "Forgot Password (Send OTP)", - "description": "Sends a 6-digit OTP to verify the worker\u0027s identity using their registered mobile number. Workers do not use email — mobile/phone is the primary identifier. OTP is valid for 10 minutes.", - "operationId": "workerForgotPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971501234567", - "description": "Worker\u0027s registered mobile number" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OTP sent successfully (response is identical whether account exists or not, to prevent enumeration)", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "message": { - "type": "string", - "example": "If an account with that mobile number exists, a reset OTP has been sent." - } - } - } - } - } - }, - "422": { - "description": "Validation error — phone is required" - } - } - } - }, - "/workers/reset-password": { - "post": { - "tags": [ - "Workers - Auth" - ], - "summary": "Reset Password (Verify OTP)", - "description": "Verifies the OTP sent to the worker\u0027s mobile number and sets a new password. The phone number must match the one used in the forgot-password step.", - "operationId": "workerResetPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "phone", - "otp", - "password", - "password_confirmation" - ], - "properties": { - "phone": { - "type": "string", - "example": "+971501234567", - "description": "Worker\u0027s registered mobile number" - }, - "otp": { - "type": "string", - "example": "482910", - "description": "6-digit OTP received" - }, - "password": { - "type": "string", - "example": "newpassword123", - "description": "New password (min 8 chars)" - }, - "password_confirmation": { - "type": "string", - "example": "newpassword123", - "description": "Must match password" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password reset successfully" - }, - "422": { - "description": "Invalid OTP, expired OTP, or validation error" - }, - "404": { - "description": "Account not found" - } - } - } - }, - "/employers/forgot-password": { - "post": { - "tags": [ - "Employers - Auth" - ], - "summary": "Forgot Password (Send OTP)", - "description": "Sends a 6-digit OTP to the employer\u0027s registered email address. OTP expires in 10 minutes.", - "operationId": "employerForgotPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "employer@example.com" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OTP sent (response identical whether account exists or not)" - }, - "422": { - "description": "Validation error" - } - } - } - }, - "/employers/reset-password": { - "post": { - "tags": [ - "Employers - Auth" - ], - "summary": "Reset Password (Verify OTP)", - "description": "Verifies the OTP sent to the employer\u0027s email and sets a new password.", - "operationId": "employerResetPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email", - "otp", - "password", - "password_confirmation" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "employer@example.com" - }, - "otp": { - "type": "string", - "example": "482910" - }, - "password": { - "type": "string", - "example": "newpassword123" - }, - "password_confirmation": { - "type": "string", - "example": "newpassword123" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password reset successfully" - }, - "422": { - "description": "Invalid or expired OTP" - } - } - } - }, - "/sponsors/forgot-password": { - "post": { - "tags": [ - "Sponsors - Auth" - ], - "summary": "Forgot Password (Send OTP)", - "description": "Sends a 6-digit OTP to the sponsor\u0027s registered email. Accepts email or mobile number as identifier. OTP expires in 10 minutes.", - "operationId": "sponsorForgotPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "sponsor@example.com", - "description": "Email address (either email or mobile required)" - }, - "mobile": { - "type": "string", - "example": "+971501234567", - "description": "Mobile number (either email or mobile required)" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OTP sent (response identical whether account exists or not)" - }, - "422": { - "description": "Validation error — email or mobile required" - } - } - } - }, - "/sponsors/reset-password": { - "post": { - "tags": [ - "Sponsors - Auth" - ], - "summary": "Reset Password (Verify OTP)", - "description": "Verifies the OTP sent to the sponsor\u0027s email and sets a new password. Use the email field to identify the account.", - "operationId": "sponsorResetPassword", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email", - "otp", - "password", - "password_confirmation" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "sponsor@example.com" - }, - "otp": { - "type": "string", - "example": "482910" - }, - "password": { - "type": "string", - "example": "newpassword123" - }, - "password_confirmation": { - "type": "string", - "example": "newpassword123" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password reset successfully" - }, - "422": { - "description": "Invalid or expired OTP" - } - } - } - } - }, - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - }, - "schemas": { - "Sponsor": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "full_name": { - "type": "string", - "example": "Mohammed Al-Rashidi" - }, - "organization_name": { - "type": "string", - "example": "Al-Rashidi Charitable Foundation" - }, - "email": { - "type": "string", - "example": "sponsor.971501112233@migrant.ae" - }, - "mobile": { - "type": "string", - "example": "+971501112233" - }, - "nationality": { - "type": "string", - "example": "UAE" - }, - "city": { - "type": "string", - "example": "Dubai" - }, - "address": { - "type": "string", - "example": "Villa 12, Jumeirah 2" - }, - "country_code": { - "type": "string", - "example": "+971" - }, - "is_verified": { - "type": "boolean", - "example": false - }, - "status": { - "type": "string", - "example": "active" - }, - "license_file": { - "type": "string", - "example": "uploads/licenses/1234567890_license_trade.pdf" - }, - "emirates_id_file": { - "type": "string", - "example": "uploads/licenses/1234567890_emirates_id.pdf" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example" - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "Worker": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 136 - }, - "name": { - "type": "string", - "example": "Amina Al-Masry" - }, - "email": { - "type": "string", - "example": "worker.971509998888@migrant.com" - }, - "phone": { - "type": "string", - "example": "+971509998888" - }, - "language": { - "type": "string", - "example": "HI" - }, - "fcm_token": { - "type": "string", - "example": "fcm_token_example" - }, - "nationality": { - "type": "string", - "example": "Egypt" - }, - "age": { - "type": "integer", - "example": 28 - }, - "salary": { - "type": "string", - "example": "1500.00" - }, - "availability": { - "type": "string", - "example": "Immediate" - }, - "experience": { - "type": "string", - "example": "4 Years" - }, - "religion": { - "type": "string", - "example": "Muslim" - }, - "bio": { - "type": "string", - "example": "Certified infant caregiver and housekeeper with excellent cooking skills." - }, - "verified": { - "type": "boolean", - "example": false - }, - "status": { - "type": "string", - "example": "active" - }, - "in_country": { - "type": "boolean", - "example": true - }, - "visa_status": { - "type": "string", - "example": "Tourist Visa" - }, - "passport_status": { - "type": "string", - "example": "Passport Verified" - }, - "emirates_id_status": { - "type": "string", - "example": "Passport Verified" - }, - "preferred_job_type": { - "type": "string", - "example": "full-time" - }, - "live_in_out": { - "type": "string", - "example": "Live-out" - }, - "gender": { - "type": "string", - "enum": [ - "male", - "female", - "other" - ], - "example": "male" - }, - "preferred_location": { - "type": "string", - "example": "Dubai Marina" - }, - "country": { - "type": "string", - "example": "UAE" - }, - "city": { - "type": "string", - "example": "Dubai" - }, - "area": { - "type": "string", - "example": "Marina" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2026-05-20T14:54:26.000000Z" - }, - "updated_at": { - "type": "string", - "format": "date-time", - "example": "2026-05-20T14:54:26.000000Z" - }, - "category": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 7 - }, - "name": { - "type": "string", - "example": "General Helper" - } - } - }, - "skills": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 6 - }, - "name": { - "type": "string", - "example": "Cleaning" - } - } - } - }, - "documents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WorkerDocument" - } - } - } - }, - "WorkerDocument": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 15 - }, - "worker_id": { - "type": "integer", - "example": 136 - }, - "type": { - "type": "string", - "example": "passport" - }, - "number": { - "type": "string", - "example": "P345892" - }, - "issue_date": { - "type": "string", - "format": "date", - "example": "2024-05-20" - }, - "expiry_date": { - "type": "string", - "format": "date", - "example": "2034-05-20" - }, - "ocr_accuracy": { - "type": "number", - "example": 98.5 - }, - "file_path": { - "type": "string", - "example": "uploads/documents/1716200000_passport_my_file.jpg" - } - } - }, - "JobOffer": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "employer_id": { - "type": "integer", - "example": 2 - }, - "worker_id": { - "type": "integer", - "example": 136 - }, - "work_date": { - "type": "string", - "format": "date", - "example": "2026-06-01" - }, - "location": { - "type": "string", - "example": "Dubai Marina, Silverene Towers" - }, - "salary": { - "type": "string", - "example": "2500.00" - }, - "notes": { - "type": "string", - "example": "Require housekeeping support starting from June 1st." - }, - "status": { - "type": "string", - "example": "pending" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2026-05-20T15:23:44.000000Z" - }, - "updated_at": { - "type": "string", - "format": "date-time", - "example": "2026-05-20T15:23:44.000000Z" - } - } + "message": { + "type": "string", + "example": "Profile updated successfully." + }, + "data": { + "type": "object", + "properties": { + "worker": { + "$ref": "#/components/schemas/Worker" + } + } + } + } + } + } + } + } + } + } + }, + "/workers/go-live": { + "post": { + "tags": [ + "Worker/Profile" + ], + "summary": "Profile Go Live", + "description": "Sets the worker status to 'active' and availability to 'Immediate', making the profile searchable on the sponsor/employer marketplace.", + "responses": { + "200": { + "description": "Worker profile is now live and searchable.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Your profile is now live! Status set to Active." + } + } + } + } + } + } + } + } + }, + "/workers/profile/toggle-availability": { + "post": { + "tags": [ + "Worker/Profile" + ], + "summary": "Toggle Profile Search Visibility", + "description": "Updates worker search visibility based on whether they are still actively seeking a job. 'still_looking' as true makes profile active, false hides it.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "still_looking" + ], + "properties": { + "still_looking": { + "type": "boolean", + "example": true, + "description": "Whether the worker is still actively looking for job offers." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Availability toggled successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "still_looking": { + "type": "boolean", + "example": true + }, + "status": { + "type": "string", + "example": "active" + } + } + } + } + } + } + } + } + }, + "/workers/profile/mark-hired": { + "post": { + "tags": [ + "Worker/Profile" + ], + "summary": "Mark Worker as Hired", + "description": "Changes the worker's status to 'Hired', which automatically removes them from active marketplace searches.", + "responses": { + "200": { + "description": "Worker marked as Hired.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Congratulations! You have been successfully marked as Hired." + } + } + } + } + } + } + } + } + }, + "/workers/offers": { + "get": { + "tags": [ + "Worker/Offers" + ], + "summary": "View Received Job Offers", + "description": "Returns a list of all custom job/hiring offers received by the worker from different employers (e.g. Work Date, location, salaries, and notes).", + "responses": { + "200": { + "description": "List of job offers retrieved.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "offers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JobOffer" } - } - } -} + } + } + } + } + } + } + } + } + } + } + }, + "/workers/offers/{id}/respond": { + "post": { + "tags": [ + "Worker/Offers" + ], + "summary": "Accept or Reject Job Offer", + "description": "Responds to a pending job offer. If accepted, the job offer status becomes 'accepted' and the worker's status is automatically changed to 'Hired', which reflects immediately in the Employer panel.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The Job Offer ID reference.", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "accepted", + "rejected" + ], + "example": "accepted", + "description": "Response action (accepted or rejected)." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Job offer response saved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Job offer successfully accepted." + }, + "data": { + "type": "object", + "properties": { + "offer": { + "$ref": "#/components/schemas/JobOffer" + }, + "worker_status": { + "type": "string", + "example": "Hired" + } + } + } + } + } + } + } + } + } + } + }, + "/workers/conversations": { + "get": { + "tags": [ + "Worker/Conversations" + ], + "summary": "Get Conversations List (Worker)", + "description": "Retrieves all active conversations for the authenticated worker.", + "responses": { + "200": { + "description": "Conversations list retrieved successfully." + } + } + } + }, + "/workers/conversations/{id}/messages": { + "get": { + "tags": [ + "Worker/Conversations" + ], + "summary": "Get Conversation Messages (Worker)", + "description": "Retrieves message history for a conversation and marks incoming employer messages as read.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Messages retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Worker/Conversations" + ], + "summary": "Send Reply Message (Worker)", + "description": "Sends a reply message from the worker to the employer.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "text": { + "type": "string", + "example": "Yes, I am available.", + "description": "Optional text message content (required if no file is uploaded)." + }, + "file": { + "type": "string", + "format": "binary", + "description": "Optional file or voice note attachment (Max 10MB). Supports images, documents, and common audio formats." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Message sent successfully." + } + } + } + }, + "/workers/report": { + "post": { + "tags": [ + "Worker/Profile" + ], + "summary": "Submit a Safety/Moderation Report (Worker)", + "description": "Submits a report on a chat conversation or a review. The report is logged in the safety queue and reviewed by administrators.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "type", + "item_id", + "reason" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Chat", + "Review" + ], + "example": "Chat", + "description": "The type of entity being reported (Chat or Review)." + }, + "item_id": { + "type": "string", + "example": "1", + "description": "The ID of the conversation or review being reported." + }, + "user_id": { + "type": "string", + "example": "5", + "description": "Optional ID of the user being reported." + }, + "reason": { + "type": "string", + "example": "Others", + "description": "The reason for submitting this report." + }, + "others": { + "type": "string", + "example": "Spam and malicious links", + "description": "Custom reason string. Used only if reason is 'Others'." + }, + "description": { + "type": "string", + "example": "The other party sent highly inappropriate and insulting messages.", + "description": "Optional detailed explanation/comments." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Report submitted successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Report submitted successfully. Our safety team will review it shortly." + }, + "report_id": { + "type": "string", + "example": "REP-ABCD1234" + } + } + } + } + } + }, + "404": { + "description": "Conversation or Review not found, or user is not authorized to report it." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/workers/report-reasons": { + "get": { + "tags": [ + "Worker/Profile" + ], + "summary": "Get Safety Report Reasons (Worker)", + "description": "Returns a list of active safety/moderation report reasons. Can filter reasons by target type (Chat or Review).", + "parameters": [ + { + "name": "type", + "in": "query", + "required": false, + "description": "Optional type to filter reasons (Chat or Review). If not provided, returns all active reasons.", + "schema": { + "type": "string", + "enum": [ + "Chat", + "Review" + ] + } + } + ], + "responses": { + "200": { + "description": "List of report reasons retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "reasons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "reason": { + "type": "string", + "example": "Abusive language / Profanity" + }, + "type": { + "type": "string", + "example": "Both" + } + } + } + } + } + } + } + } + } + } + } + }, + "/workers/tickets": { + "get": { + "tags": [ + "Worker/Support" + ], + "summary": "List Support Tickets (Worker)", + "description": "Retrieves a paginated list of all support tickets created by the authenticated worker.", + "responses": { + "200": { + "description": "Tickets retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Worker/Support" + ], + "summary": "Create Support Ticket (Worker)", + "description": "Creates a support ticket for a worker with a subject, description, and optional priority level.", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "subject", + "description" + ], + "properties": { + "subject": { + "type": "string", + "example": "App login crashes on launch" + }, + "description": { + "type": "string", + "example": "When opening the app on my phone, it crashes immediately on the splash screen." + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "reason_id": { + "type": "integer", + "example": 1, + "description": "Optional report/support reason ID." + }, + "voice_note": { + "type": "string", + "format": "binary", + "description": "Optional audio/voice note file attachment." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Ticket created successfully." + } + } + } + }, + "/workers/tickets/{id}": { + "get": { + "tags": [ + "Worker/Support" + ], + "summary": "Get Support Ticket Details & Replies (Worker)", + "description": "Retrieves the details of a support ticket and its chronological reply history.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Ticket details and replies retrieved successfully." + } + } + } + }, + "/workers/tickets/{id}/reply": { + "post": { + "tags": [ + "Worker/Support" + ], + "summary": "Reply to Support Ticket (Worker)", + "description": "Submits a text reply or message to an active, open, or resolved support ticket.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "I tried clearing cache and it still crashes." + }, + "voice_note": { + "type": "string", + "format": "binary", + "description": "Optional audio/voice note file attachment." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Reply posted successfully." + } + } + } + }, + "/employers/register": { + "post": { + "tags": [ + "Employer/Auth" + ], + "summary": "Register Sponsor (Alias)", + "description": "Legacy route matching the simplified sponsor registration (Name, Email, Phone) (Step 1).", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name", + "email", + "phone" + ], + "properties": { + "name": { + "type": "string", + "example": "Ahmad Bin Ahmed", + "description": "Sponsor's full name" + }, + "email": { + "type": "string", + "example": "ahmad@example.com", + "description": "Sponsor's contact email address" + }, + "phone": { + "type": "string", + "example": "+971509990001", + "description": "Sponsor's mobile phone number" + }, + "emirates_id": { + "type": "object", + "description": "Optional JSON object containing extracted Emirates ID details.", + "properties": { + "emirates_id_number": { + "type": "string", + "example": "784-1988-5310327-2" + }, + "name": { + "type": "string", + "example": "Ahmad Bin Ahmed" + }, + "date_of_birth": { + "type": "string", + "example": "1990-01-01" + }, + "nationality": { + "type": "string", + "example": "Emirati" + }, + "issue_date": { + "type": "string", + "example": "2023-04-11" + }, + "expiry_date": { + "type": "string", + "example": "2028-04-11" + }, + "employer": { + "type": "string", + "example": "Federal Authority" + }, + "issue_place": { + "type": "string", + "example": "Abu Dhabi" + }, + "occupation": { + "type": "string", + "example": "Manager" + } + } + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + }, + "address": { + "type": "string", + "example": "Villa 45, Street 12", + "description": "Physical address/location details." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Sponsor registered successfully." + } + } + } + }, + "/employers/verify": { + "post": { + "tags": [ + "Employer/Auth" + ], + "summary": "Verify Sponsor Email OTP", + "description": "Verifies the email address with the OTP verification code (Step 2).", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email", + "otp" + ], + "properties": { + "email": { + "type": "string", + "example": "ahmad@example.com" + }, + "otp": { + "type": "string", + "example": "111111", + "description": "6-digit OTP code" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Email verified successfully." + } + } + } + }, + "/employers/payment": { + "post": { + "tags": [ + "Employer/Auth" + ], + "summary": "Sponsor Subscription Payment", + "description": "Submits a successful plan selection and PayTabs payment confirmation (Step 4).", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email", + "plan_id", + "amount_aed", + "paytabs_transaction_id" + ], + "properties": { + "email": { + "type": "string", + "example": "ahmad@example.com" + }, + "plan_id": { + "type": "string", + "example": "premium" + }, + "amount_aed": { + "type": "number", + "example": 199 + }, + "paytabs_transaction_id": { + "type": "string", + "example": "TXN998877" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Subscription payment confirmed." + } + } + } + }, + "/employers/password": { + "post": { + "tags": [ + "Employer/Auth" + ], + "summary": "Create Sponsor Account Password", + "description": "Configures and finalizes the portal login password to complete registration (Step 5). Returns bearer token.", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email", + "password", + "password_confirmation" + ], + "properties": { + "email": { + "type": "string", + "example": "ahmad@example.com" + }, + "password": { + "type": "string", + "format": "password", + "example": "Password@123" + }, + "password_confirmation": { + "type": "string", + "format": "password", + "example": "Password@123" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Password set and registration finalized successfully." + } + } + } + }, + "/employers/plans": { + "get": { + "tags": [ + "Employer/Auth" + ], + "summary": "Get Available Subscription Plans", + "description": "Returns details (price, features) of subscription packages available to sponsors.", + "security": [], + "responses": { + "200": { + "description": "Subscription plans list retrieved successfully." + } + } + } + }, + "/employers/login": { + "post": { + "tags": [ + "Employer/Auth" + ], + "summary": "Authenticate Employer or Sponsor", + "description": "Unified authentication endpoint. Validates email/mobile and password, then returns a stateless Bearer token and the user's role (employer or sponsor).", + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "password" + ], + "properties": { + "email": { + "type": "string", + "example": "ahmad@example.com", + "description": "Email address (required if mobile is not provided)" + }, + "mobile": { + "type": "string", + "example": "+971509990001", + "description": "Mobile number (required if email is not provided)" + }, + "password": { + "type": "string", + "example": "Password@123" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Login successful. Returns Bearer token and role.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Login successful." + }, + "role": { + "type": "string", + "example": "employer", + "enum": [ + "employer", + "sponsor" + ] + }, + "data": { + "type": "object", + "properties": { + "token": { + "type": "string", + "example": "api_token_here" + } + } + } + } + } + } + } + } + } + } + }, + "/employers/conversations": { + "get": { + "tags": [ + "Employer/Conversations" + ], + "summary": "Get Conversations List (Employer)", + "description": "Retrieves all active candidate conversations for the authenticated employer.", + "responses": { + "200": { + "description": "Conversations list retrieved successfully." + } + } + } + }, + "/employers/conversations/{id}/messages": { + "get": { + "tags": [ + "Employer/Conversations" + ], + "summary": "Get Conversation Messages (Employer)", + "description": "Retrieves message history for a conversation and marks incoming worker messages as read.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Messages retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Employer/Conversations" + ], + "summary": "Send Reply Message (Employer)", + "description": "Sends a reply message from the employer to the worker.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "text": { + "type": "string", + "example": "When can you start?", + "description": "Optional text message content (required if no file is uploaded)." + }, + "file": { + "type": "string", + "format": "binary", + "description": "Optional file or voice note attachment (Max 10MB). Supports images, documents, and common audio formats." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Message sent successfully." + } + } + } + }, + "/employers/conversations/start": { + "post": { + "tags": [ + "Employer/Conversations" + ], + "summary": "Start Conversation with Worker", + "description": "Starts or retrieves a conversation with the specified worker ID.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "worker_id" + ], + "properties": { + "worker_id": { + "type": "integer", + "example": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Conversation started successfully." + } + } + } + }, + "/workers/announcements": { + "get": { + "tags": [ + "Worker/CharityEvents" + ], + "summary": "Get Charity Events (Worker)", + "description": "Allows workers to retrieve the list of active charity events, food drives, and medical support programs scheduled in Dubai.", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "List of charity events retrieved successfully." + } + } + } + }, + "/workers/announcements/new": { + "get": { + "tags": [ + "Worker/CharityEvents" + ], + "summary": "Get New/Unviewed Announcements/Charity Events", + "description": "Retrieves up to 3 active approved charity events/announcements that the worker has not yet viewed, and automatically marks them as viewed.", + "responses": { + "200": { + "description": "Successfully retrieved new announcements.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 4 + }, + "title": { + "type": "string", + "example": "Free Dental Checkup Camp" + }, + "body": { + "type": "string", + "example": "Emirates Charity is providing free screening." + }, + "type": { + "type": "string", + "example": "charity" + }, + "employer_name": { + "type": "string", + "example": "Emirates Charity" + }, + "company_name": { + "type": "string", + "example": "Emirates Charity Foundation" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2026-06-01T10:00:00.000000Z" + }, + "time_ago": { + "type": "string", + "example": "5 hours ago" + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthenticated." + } + } + } + }, + "/workers/announcements/{id}/view": { + "post": { + "tags": [ + "Worker/CharityEvents" + ], + "summary": "Mark Specific Announcement as Viewed", + "description": "Explicitly marks a specific announcement/charity event as viewed by the authenticated worker.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Announcement ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Announcement marked as viewed successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Announcement marked as viewed." + } + } + } + } + } + }, + "401": { + "description": "Unauthenticated." + }, + "404": { + "description": "Announcement not found." + } + } + } + }, + "/employers/announcements": { + "get": { + "tags": [ + "Employer/CharityEvents" + ], + "summary": "Get Posted Charity Events (Employer)", + "description": "Allows employers/sponsors to retrieve the list of charity events they have published.", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "List of posted charity events retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Employer/CharityEvents" + ], + "summary": "Publish Charity Event (Employer)", + "description": "Allows employers/sponsors to publish a new community charity event or drive to all workers in Dubai, triggering instant push notifications and morning-of reminders.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "title", + "content", + "provided_items", + "event_date", + "event_time", + "location_details", + "location_pin" + ], + "properties": { + "title": { + "type": "string", + "example": "Free Dental Checkup Camp" + }, + "content": { + "type": "string", + "example": "Emirates Charity is providing free professional dental screening, cleanings, and educational packages for domestic helpers." + }, + "provided_items": { + "type": "string", + "example": "Free Dental screening, cleanings, and wellness kits" + }, + "event_date": { + "type": "string", + "format": "date", + "example": "2026-06-15" + }, + "event_time": { + "type": "string", + "example": "9:00 AM - 4:00 PM" + }, + "location_details": { + "type": "string", + "example": "Al Quoz Community Hall, Dubai" + }, + "location_pin": { + "type": "string", + "format": "uri", + "example": "https://maps.app.goo.gl/xyz" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Charity event created and notifications scheduled successfully." + } + } + } + }, + "/employers/announcements/{id}": { + "delete": { + "tags": [ + "Employer/CharityEvents" + ], + "summary": "Delete Charity Event (Employer)", + "description": "Allows employers/sponsors to delete a previously published charity event.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Charity event deleted successfully." + } + } + } + }, + "/employers/dashboard": { + "get": { + "tags": [ + "Employer/Profile" + ], + "summary": "Get Employer Dashboard stats & recent events", + "description": "Retrieves stats including contacted workers count, total hired workers, saved candidates, current plan details, and recent announcements or events.", + "responses": { + "200": { + "description": "Employer dashboard stats retrieved successfully." + } + } + } + }, + "/employers/profile": { + "get": { + "tags": [ + "Employer/Profile" + ], + "summary": "Get Profile details (Employer)", + "description": "Retrieves the authenticated employer's profile details including their selected company name, phone, language preference, and notification settings.", + "responses": { + "200": { + "description": "Employer profile details retrieved successfully." + } + } + } + }, + "/employers/profile/update": { + "post": { + "tags": [ + "Employer/Profile" + ], + "summary": "Update Profile details (Employer)", + "description": "Allows employers to update their profile details (company name, phone number, name, email, language preference, and notification settings) and change their account password.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name", + "email", + "phone", + "company_name", + "language", + "notifications" + ], + "properties": { + "name": { + "type": "string", + "example": "Ahmad" + }, + "email": { + "type": "string", + "example": "ahmad@example.com" + }, + "phone": { + "type": "string", + "example": "+971509990001" + }, + "company_name": { + "type": "string", + "example": "Ahmad Tech Ltd" + }, + "language": { + "type": "string", + "enum": [ + "English", + "Arabic", + "english", + "arabic" + ], + "example": "English" + }, + "notifications": { + "type": "boolean", + "example": true + }, + "current_password": { + "type": "string", + "example": "Password@123" + }, + "new_password": { + "type": "string", + "example": "NewSecurePassword@123" + }, + "new_password_confirmation": { + "type": "string", + "example": "NewSecurePassword@123" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example", + "description": "Firebase Cloud Messaging token for push notifications." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Profile updated successfully." + } + } + } + }, + "/employers/workers": { + "get": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Get Available Workers", + "description": "Returns a list of verified workers that are currently available to be hired.", + "parameters": [ + { + "name": "search", + "in": "query", + "required": false, + "description": "Search term matching helper name, nationality, or religion.", + "schema": { + "type": "string" + } + }, + { + "name": "skills", + "in": "query", + "required": false, + "description": "Filter by skills (comma-separated list).", + "schema": { + "type": "string" + } + }, + { + "name": "language", + "in": "query", + "required": false, + "description": "Filter by language.", + "schema": { + "type": "string" + } + }, + { + "name": "nationality", + "in": "query", + "required": false, + "description": "Filter by nationality name (comma-separated list for multiple choice, e.g. Indian,Kenyan,Ethiopian).", + "schema": { + "type": "string" + } + }, + { + "name": "gender", + "in": "query", + "required": false, + "description": "Filter by gender (e.g. male, female).", + "schema": { + "type": "string" + } + }, + { + "name": "preferred_location", + "in": "query", + "required": false, + "description": "Filter by preferred location (comma-separated list, e.g. Dubai,Abu Dhabi).", + "schema": { + "type": "string" + } + }, + { + "name": "area", + "in": "query", + "required": false, + "description": "Filter by preferred area (comma-separated list, e.g. Marina,Al Barsha).", + "schema": { + "type": "string" + } + }, + { + "name": "job_type", + "in": "query", + "required": false, + "description": "Filter by job type (comma-separated list, e.g. full-time,part-time).", + "schema": { + "type": "string" + } + }, + { + "name": "accommodation_type", + "in": "query", + "required": false, + "description": "Filter by accommodation type (comma-separated list, e.g. live_in,live_out).", + "schema": { + "type": "string" + } + }, + { + "name": "in_country", + "in": "query", + "required": false, + "description": "Filter by in country status (true/false or yes/no or in/out).", + "schema": { + "type": "string" + } + }, + { + "name": "out_country", + "in": "query", + "required": false, + "description": "Filter by out country status (true/false or yes/no or out/in).", + "schema": { + "type": "string" + } + }, + { + "name": "visa_status", + "in": "query", + "required": false, + "description": "Filter by next visa status (comma-separated list, e.g. Residence Visa,Tourist Visa). Only applicable if in country.", + "schema": { + "type": "string" + } + }, + { + "name": "availability", + "in": "query", + "required": false, + "description": "Filter by availability status.", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "Available workers list retrieved successfully." + } + } + } + }, + "/employers/workers/{id}": { + "get": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Get Worker Profile Detail", + "description": "Retrieves the complete profile details for a worker, mirroring the web detailed view, along with active conversation details if already started with this employer.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Worker ID reference.", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Worker profile details retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "worker": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "Jane Doe" + }, + "nationality": { + "type": "string", + "example": "Filipino" + }, + "photo": { + "type": "string", + "example": "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=200" + }, + "emirates_id_status": { + "type": "string", + "example": "Passport Verified", + "description": "Deprecated - Use passport_status instead" + }, + "passport_status": { + "type": "string", + "example": "Passport Verified" + }, + "category": { + "type": "string", + "example": "Domestic Worker" + }, + "skills": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "childcare", + "cooking" + ] + }, + "availability_status": { + "type": "string", + "example": "Active" + }, + "visa_status": { + "type": "string", + "example": "Residence Visa" + }, + "experience": { + "type": "string", + "example": "5 Years" + }, + "experience_years": { + "type": "integer", + "example": 5 + }, + "salary": { + "type": "integer", + "example": 2500 + }, + "religion": { + "type": "string", + "example": "Christian" + }, + "languages": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "English", + "Tagalog" + ] + }, + "age": { + "type": "integer", + "example": 28 + }, + "verified": { + "type": "boolean", + "example": true + }, + "preferred_job_type": { + "type": "string", + "example": "live-in" + }, + "bio": { + "type": "string", + "example": "Experienced and caring domestic worker specialing in childcare and housekeeping..." + }, + "rating": { + "type": "number", + "example": 4.3 + }, + "reviews_count": { + "type": "integer", + "example": 6 + }, + "reviews": { + "type": "array", + "items": { + "type": "object" + } + }, + "similar_workers": { + "type": "array", + "items": { + "type": "object" + } + }, + "conversation_id": { + "type": "integer", + "nullable": true, + "example": 4 + } + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Worker profile not found." + } + } + } + }, + "/employers/candidates": { + "get": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Get Candidate Applications and Offers", + "description": "Returns all job applications and direct hiring offers connected to the authenticated sponsor account that have achieved 'Hired' status.", + "parameters": [ + { + "name": "search", + "in": "query", + "required": false, + "description": "Search term matching helper name, nationality, or religion.", + "schema": { + "type": "string" + } + }, + { + "name": "skills", + "in": "query", + "required": false, + "description": "Filter candidates by skills (comma-separated list of skill names, e.g. cooking,driving).", + "schema": { + "type": "string" + } + }, + { + "name": "languages", + "in": "query", + "required": false, + "description": "Filter candidates by languages spoken (comma-separated list of language names, e.g. English,Arabic).", + "schema": { + "type": "string" + } + }, + { + "name": "nationality", + "in": "query", + "required": false, + "description": "Filter candidates by nationality name (comma-separated list for multiple choice, e.g. Indian,Kenyan,Ethiopian).", + "schema": { + "type": "string" + } + }, + { + "name": "gender", + "in": "query", + "required": false, + "description": "Filter candidates by gender (e.g. male, female).", + "schema": { + "type": "string" + } + }, + { + "name": "preferred_location", + "in": "query", + "required": false, + "description": "Filter candidates by preferred location (comma-separated list, e.g. Dubai,Abu Dhabi).", + "schema": { + "type": "string" + } + }, + { + "name": "job_type", + "in": "query", + "required": false, + "description": "Filter candidates by job type (comma-separated list, e.g. full-time,part-time).", + "schema": { + "type": "string" + } + }, + { + "name": "accommodation_type", + "in": "query", + "required": false, + "description": "Filter candidates by accommodation type (comma-separated list, e.g. live_in,live_out).", + "schema": { + "type": "string" + } + }, + { + "name": "in_country", + "in": "query", + "required": false, + "description": "Filter candidates by in country status (true/false or yes/no or in/out).", + "schema": { + "type": "string" + } + }, + { + "name": "out_country", + "in": "query", + "required": false, + "description": "Filter candidates by out country status (true/false or yes/no or out/in).", + "schema": { + "type": "string" + } + }, + { + "name": "visa_status", + "in": "query", + "required": false, + "description": "Filter candidates by next visa status (comma-separated list, e.g. Residence Visa,Tourist Visa). Only applicable if in country.", + "schema": { + "type": "string" + } + }, + { + "name": "availability", + "in": "query", + "required": false, + "description": "Filter candidates by availability status.", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "Employer candidate pipeline retrieved successfully." + } + } + } + }, + "/employers/candidates/{id}/hire": { + "post": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Mark Candidate as Hired", + "description": "Transitions the target candidate application, job offer, or helper status to 'Hired'.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The target identifier (Application ID, Offer ID, or Worker ID).", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Candidate successfully status updated to Hired." + } + } + } + }, + "/employers/shortlist": { + "get": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Get Shortlisted Workers", + "description": "Returns a list of all workers shortlisted by the authenticated sponsor.", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "Shortlisted workers retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Employer/Pipeline" + ], + "summary": "Toggle Worker Shortlist Status", + "description": "Toggles (adds or removes) a worker to/from the authenticated sponsor's shortlist.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "worker_id" + ], + "properties": { + "worker_id": { + "type": "integer", + "example": 1, + "description": "Worker ID to shortlist/unshortlist." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Shortlist status toggled successfully." + } + } + } + }, + "/employers/payments": { + "get": { + "tags": [ + "Employer/Billing" + ], + "summary": "Get Payment History", + "description": "Retrieves the complete transaction history for the authenticated sponsor/employer, including subscription passes and document verification receipts.", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "Employer billing history retrieved successfully." + } + } + } + }, + "/workers/dashboard/views": { + "get": { + "tags": [ + "Worker/Dashboard" + ], + "summary": "Get Worker Profile Views Statistics", + "description": "Allows authenticated workers to see the total count and detailed list of employers/sponsors who viewed their full profile.", + "responses": { + "200": { + "description": "Profile views statistics retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "views_count": { + "type": "integer", + "example": 5 + }, + "views_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "employer_id": { + "type": "integer", + "example": 2 + }, + "employer_name": { + "type": "string", + "example": "Ahmad" + }, + "company_name": { + "type": "string", + "example": "Ahmad Tech Ltd" + }, + "nationality": { + "type": "string", + "example": "UAE" + }, + "city": { + "type": "string", + "example": "Dubai" + }, + "viewed_at": { + "type": "string", + "format": "date-time", + "example": "2026-06-01T15:10:24.000000Z" + }, + "viewed_at_formatted": { + "type": "string", + "example": "2 minutes ago" + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/workers/dashboard": { + "get": { + "tags": [ + "Worker/Dashboard" + ], + "summary": "Get Worker Consolidated Dashboard Data", + "description": "Allows authenticated workers to retrieve their dashboard metrics: active status, profile views count, currently working sponsor/employer details, and latest charity events/announcements.", + "responses": { + "200": { + "description": "Worker dashboard details retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "object", + "properties": { + "active_status": { + "type": "string", + "example": "active" + }, + "count_sponsors_viewed": { + "type": "integer", + "example": 5 + }, + "employer_contacted": { + "type": "integer", + "example": 2, + "description": "Unique count of employers who contacted this worker." + }, + "profile_viewed": { + "type": "integer", + "example": 3, + "description": "Unique count of employers who viewed this worker's profile." + }, + "currently_working_sponsor": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "integer", + "example": 2 + }, + "name": { + "type": "string", + "example": "Ahmad" + }, + "company_name": { + "type": "string", + "example": "Ahmad Tech Ltd" + }, + "nationality": { + "type": "string", + "example": "UAE" + }, + "city": { + "type": "string", + "example": "Dubai" + }, + "email": { + "type": "string", + "example": "ahmad@example.com" + }, + "phone": { + "type": "string", + "example": "+971509990001" + }, + "hired_at": { + "type": "string", + "format": "date-time", + "example": "2026-06-01T15:10:24.000000Z" + }, + "hired_at_formatted": { + "type": "string", + "example": "Jun 01, 2026" + } + } + }, + "latest_charity_events_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Free Dental Checkup Camp" + }, + "body": { + "type": "string", + "example": "Emirates Charity is providing free screening." + }, + "type": { + "type": "string", + "example": "charity" + }, + "employer_name": { + "type": "string", + "example": "Emirates Charity" + }, + "company_name": { + "type": "string", + "example": "Emirates Charity Foundation" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2026-06-01T10:00:00.000000Z" + }, + "time_ago": { + "type": "string", + "example": "5 hours ago" + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/employers/reviews": { + "get": { + "tags": [ + "Employer/Reviews" + ], + "summary": "Get Reviews Written by Employer", + "description": "Retrieves a paginated list of all reviews written by the authenticated employer.", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "description": "Page number.", + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "required": false, + "description": "Number of items per page.", + "schema": { + "type": "integer", + "default": 15 + } + } + ], + "responses": { + "200": { + "description": "Reviews retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Employer/Reviews" + ], + "summary": "Add or Update Worker Review", + "description": "Allows employers to submit a review score and description comment for a worker. If the employer has already left a review, this will update it.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "worker_id", + "rating" + ], + "properties": { + "worker_id": { + "type": "integer", + "example": 136, + "description": "Worker ID being reviewed." + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "example": 5, + "description": "Review score from 1 to 5." + }, + "comment": { + "type": "string", + "example": "Highly professional, punctual and did a great job!", + "description": "Optional detailed comment." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Review updated successfully." + }, + "201": { + "description": "Review added successfully." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/employers/reviews/{id}": { + "put": { + "tags": [ + "Employer/Reviews" + ], + "summary": "Edit Worker Review", + "description": "Allows employers to edit their previously created review score and comment by review ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Review ID reference.", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "rating" + ], + "properties": { + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "example": 4, + "description": "Updated rating score from 1 to 5." + }, + "comment": { + "type": "string", + "example": "Updated feedback comment.", + "description": "Updated review comment description." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Review edited successfully." + }, + "404": { + "description": "Review not found or unauthorized." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/employers/report": { + "post": { + "tags": [ + "Employer/Reviews" + ], + "summary": "Submit a Safety/Moderation Report (Employer)", + "description": "Submits a report on a chat conversation or a review. The report is logged in the safety queue and reviewed by administrators.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "type", + "item_id", + "reason" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Chat", + "Review" + ], + "example": "Chat", + "description": "The type of entity being reported (Chat or Review)." + }, + "item_id": { + "type": "string", + "example": "1", + "description": "The ID of the conversation or review being reported." + }, + "user_id": { + "type": "string", + "example": "5", + "description": "Optional ID of the user being reported." + }, + "reason": { + "type": "string", + "example": "Others", + "description": "The reason for submitting this report." + }, + "others": { + "type": "string", + "example": "Spam and malicious links", + "description": "Custom reason string. Used only if reason is 'Others'." + }, + "description": { + "type": "string", + "example": "The worker requested payments off-platform and sent abusive messages.", + "description": "Optional detailed explanation/comments." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Report submitted successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Report submitted successfully. Our safety team will review it shortly." + }, + "report_id": { + "type": "string", + "example": "REP-ABCD1234" + } + } + } + } + } + }, + "404": { + "description": "Conversation or Review not found, or user is not authorized to report it." + }, + "422": { + "description": "Validation error." + } + } + } + }, + "/employers/report-reasons": { + "get": { + "tags": [ + "Employer/Reviews" + ], + "summary": "Get Safety Report Reasons (Employer)", + "description": "Returns a list of active safety/moderation report reasons. Can filter reasons by target type (Chat or Review).", + "parameters": [ + { + "name": "type", + "in": "query", + "required": false, + "description": "Optional type to filter reasons (Chat or Review). If not provided, returns all active reasons.", + "schema": { + "type": "string", + "enum": [ + "Chat", + "Review" + ] + } + } + ], + "responses": { + "200": { + "description": "List of report reasons retrieved successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "reasons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "reason": { + "type": "string", + "example": "Abusive language / Profanity" + }, + "type": { + "type": "string", + "example": "Both" + } + } + } + } + } + } + } + } + } + } + } + }, + "/employers/tickets": { + "get": { + "tags": [ + "Employer/Support" + ], + "summary": "List Support Tickets (Employer)", + "description": "Retrieves a paginated list of all support tickets created by the authenticated employer.", + "responses": { + "200": { + "description": "Tickets retrieved successfully." + } + } + }, + "post": { + "tags": [ + "Employer/Support" + ], + "summary": "Create Support Ticket (Employer)", + "description": "Creates a support ticket for an employer with a subject, description, and optional priority level.", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "subject", + "description" + ], + "properties": { + "subject": { + "type": "string", + "example": "Payment card rejected" + }, + "description": { + "type": "string", + "example": "My Visa card was declined when trying to buy the Premium Employer Pass." + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "reason_id": { + "type": "integer", + "example": 1, + "description": "Optional report/support reason ID." + }, + "voice_note": { + "type": "string", + "format": "binary", + "description": "Optional audio/voice note file attachment." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Ticket created successfully." + } + } + } + }, + "/employers/tickets/{id}": { + "get": { + "tags": [ + "Employer/Support" + ], + "summary": "Get Support Ticket Details & Replies (Employer)", + "description": "Retrieves the details of a support ticket and its chronological reply history.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Ticket details and replies retrieved successfully." + } + } + } + }, + "/employers/tickets/{id}/reply": { + "post": { + "tags": [ + "Employer/Support" + ], + "summary": "Reply to Support Ticket (Employer)", + "description": "Submits a text reply or message to an active, open, or resolved support ticket.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "I tried a different card and it still fails." + }, + "voice_note": { + "type": "string", + "format": "binary", + "description": "Optional audio/voice note file attachment." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Reply posted successfully." + } + } + } + }, + "/workers/forgot-password": { + "post": { + "tags": [ + "Workers - Auth" + ], + "summary": "Forgot Password (Send OTP)", + "description": "Sends a 6-digit OTP to verify the worker's identity using their registered mobile number. Workers do not use email — mobile/phone is the primary identifier. OTP is valid for 10 minutes.", + "operationId": "workerForgotPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971501234567", + "description": "Worker's registered mobile number" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OTP sent successfully (response is identical whether account exists or not, to prevent enumeration)", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "If an account with that mobile number exists, a reset OTP has been sent." + } + } + } + } + } + }, + "422": { + "description": "Validation error — phone is required" + } + } + } + }, + "/workers/reset-password": { + "post": { + "tags": [ + "Workers - Auth" + ], + "summary": "Reset Password (Verify OTP)", + "description": "Verifies the OTP sent to the worker's mobile number and sets a new password. The phone number must match the one used in the forgot-password step.", + "operationId": "workerResetPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "phone", + "otp", + "password", + "password_confirmation" + ], + "properties": { + "phone": { + "type": "string", + "example": "+971501234567", + "description": "Worker's registered mobile number" + }, + "otp": { + "type": "string", + "example": "482910", + "description": "6-digit OTP received" + }, + "password": { + "type": "string", + "example": "newpassword123", + "description": "New password (min 8 chars)" + }, + "password_confirmation": { + "type": "string", + "example": "newpassword123", + "description": "Must match password" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Password reset successfully" + }, + "422": { + "description": "Invalid OTP, expired OTP, or validation error" + }, + "404": { + "description": "Account not found" + } + } + } + }, + "/employers/forgot-password": { + "post": { + "tags": [ + "Employers - Auth" + ], + "summary": "Forgot Password (Send OTP)", + "description": "Sends a 6-digit OTP to the employer's registered email address. OTP expires in 10 minutes.", + "operationId": "employerForgotPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "employer@example.com" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OTP sent (response identical whether account exists or not)" + }, + "422": { + "description": "Validation error" + } + } + } + }, + "/employers/reset-password": { + "post": { + "tags": [ + "Employers - Auth" + ], + "summary": "Reset Password (Verify OTP)", + "description": "Verifies the OTP sent to the employer's email and sets a new password.", + "operationId": "employerResetPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email", + "otp", + "password", + "password_confirmation" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "employer@example.com" + }, + "otp": { + "type": "string", + "example": "482910" + }, + "password": { + "type": "string", + "example": "newpassword123" + }, + "password_confirmation": { + "type": "string", + "example": "newpassword123" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Password reset successfully" + }, + "422": { + "description": "Invalid or expired OTP" + } + } + } + }, + "/sponsors/forgot-password": { + "post": { + "tags": [ + "Sponsors - Auth" + ], + "summary": "Forgot Password (Send OTP)", + "description": "Sends a 6-digit OTP to the sponsor's registered email. Accepts email or mobile number as identifier. OTP expires in 10 minutes.", + "operationId": "sponsorForgotPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "sponsor@example.com", + "description": "Email address (either email or mobile required)" + }, + "mobile": { + "type": "string", + "example": "+971501234567", + "description": "Mobile number (either email or mobile required)" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OTP sent (response identical whether account exists or not)" + }, + "422": { + "description": "Validation error — email or mobile required" + } + } + } + }, + "/sponsors/reset-password": { + "post": { + "tags": [ + "Sponsors - Auth" + ], + "summary": "Reset Password (Verify OTP)", + "description": "Verifies the OTP sent to the sponsor's email and sets a new password. Use the email field to identify the account.", + "operationId": "sponsorResetPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "email", + "otp", + "password", + "password_confirmation" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "sponsor@example.com" + }, + "otp": { + "type": "string", + "example": "482910" + }, + "password": { + "type": "string", + "example": "newpassword123" + }, + "password_confirmation": { + "type": "string", + "example": "newpassword123" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Password reset successfully" + }, + "422": { + "description": "Invalid or expired OTP" + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "Sponsor": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "full_name": { + "type": "string", + "example": "Mohammed Al-Rashidi" + }, + "organization_name": { + "type": "string", + "example": "Al-Rashidi Charitable Foundation" + }, + "email": { + "type": "string", + "example": "sponsor.971501112233@migrant.ae" + }, + "mobile": { + "type": "string", + "example": "+971501112233" + }, + "nationality": { + "type": "string", + "example": "UAE" + }, + "city": { + "type": "string", + "example": "Dubai" + }, + "address": { + "type": "string", + "example": "Villa 12, Jumeirah 2" + }, + "country_code": { + "type": "string", + "example": "+971" + }, + "is_verified": { + "type": "boolean", + "example": false + }, + "status": { + "type": "string", + "example": "active" + }, + "license_file": { + "type": "string", + "example": "uploads/licenses/1234567890_license_trade.pdf" + }, + "emirates_id_file": { + "type": "string", + "example": "uploads/licenses/1234567890_emirates_id.pdf" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "Worker": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 136 + }, + "name": { + "type": "string", + "example": "Amina Al-Masry" + }, + "email": { + "type": "string", + "example": "worker.971509998888@migrant.com" + }, + "phone": { + "type": "string", + "example": "+971509998888" + }, + "language": { + "type": "string", + "example": "HI" + }, + "fcm_token": { + "type": "string", + "example": "fcm_token_example" + }, + "nationality": { + "type": "string", + "example": "Egypt" + }, + "age": { + "type": "integer", + "example": 28 + }, + "salary": { + "type": "string", + "example": "1500.00" + }, + "availability": { + "type": "string", + "example": "Immediate" + }, + "experience": { + "type": "string", + "example": "4 Years" + }, + "religion": { + "type": "string", + "example": "Muslim" + }, + "bio": { + "type": "string", + "example": "Certified infant caregiver and housekeeper with excellent cooking skills." + }, + "verified": { + "type": "boolean", + "example": false + }, + "status": { + "type": "string", + "example": "active" + }, + "in_country": { + "type": "boolean", + "example": true + }, + "visa_status": { + "type": "string", + "example": "Tourist Visa" + }, + "passport_status": { + "type": "string", + "example": "Passport Verified" + }, + "emirates_id_status": { + "type": "string", + "example": "Passport Verified" + }, + "preferred_job_type": { + "type": "string", + "example": "full-time" + }, + "live_in_out": { + "type": "string", + "example": "Live-out" + }, + "gender": { + "type": "string", + "enum": [ + "male", + "female", + "other" + ], + "example": "male" + }, + "preferred_location": { + "type": "string", + "example": "Dubai Marina" + }, + "country": { + "type": "string", + "example": "UAE" + }, + "city": { + "type": "string", + "example": "Dubai" + }, + "area": { + "type": "string", + "example": "Marina" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2026-05-20T14:54:26.000000Z" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": "2026-05-20T14:54:26.000000Z" + }, + "category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 7 + }, + "name": { + "type": "string", + "example": "General Helper" + } + } + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 6 + }, + "name": { + "type": "string", + "example": "Cleaning" + } + } + } + }, + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WorkerDocument" + } + } + } + }, + "WorkerDocument": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 15 + }, + "worker_id": { + "type": "integer", + "example": 136 + }, + "type": { + "type": "string", + "example": "passport" + }, + "number": { + "type": "string", + "example": "P345892" + }, + "issue_date": { + "type": "string", + "format": "date", + "example": "2024-05-20" + }, + "expiry_date": { + "type": "string", + "format": "date", + "example": "2034-05-20" + }, + "ocr_accuracy": { + "type": "number", + "example": 98.5 + }, + "file_path": { + "type": "string", + "example": "uploads/documents/1716200000_passport_my_file.jpg" + } + } + }, + "JobOffer": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "employer_id": { + "type": "integer", + "example": 2 + }, + "worker_id": { + "type": "integer", + "example": 136 + }, + "work_date": { + "type": "string", + "format": "date", + "example": "2026-06-01" + }, + "location": { + "type": "string", + "example": "Dubai Marina, Silverene Towers" + }, + "salary": { + "type": "string", + "example": "2500.00" + }, + "notes": { + "type": "string", + "example": "Require housekeeping support starting from June 1st." + }, + "status": { + "type": "string", + "example": "pending" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2026-05-20T15:23:44.000000Z" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": "2026-05-20T15:23:44.000000Z" + } + } + } + } + } +} \ No newline at end of file diff --git a/public/uploads/licenses/1780656858_license_license.pdf b/public/uploads/licenses/1780656858_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780656874_license_license.pdf b/public/uploads/licenses/1780656874_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780656902_license_license.pdf b/public/uploads/licenses/1780656902_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780657376_license_license.pdf b/public/uploads/licenses/1780657376_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780658014_license_license.pdf b/public/uploads/licenses/1780658014_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780910940_license_license.pdf b/public/uploads/licenses/1780910940_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780911257_license_license.pdf b/public/uploads/licenses/1780911257_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780911277_license_license.pdf b/public/uploads/licenses/1780911277_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780911292_license_license.pdf b/public/uploads/licenses/1780911292_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780911308_license_license.pdf b/public/uploads/licenses/1780911308_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780911321_license_license.pdf b/public/uploads/licenses/1780911321_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780912272_license_license.pdf b/public/uploads/licenses/1780912272_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780912872_license_license.pdf b/public/uploads/licenses/1780912872_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780914822_license_license.pdf b/public/uploads/licenses/1780914822_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988093_license_license.pdf b/public/uploads/licenses/1780988093_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988183_license_license.pdf b/public/uploads/licenses/1780988183_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988391_license_license.pdf b/public/uploads/licenses/1780988391_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988597_license_license.pdf b/public/uploads/licenses/1780988597_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988676_license_license.pdf b/public/uploads/licenses/1780988676_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988798_license_license.pdf b/public/uploads/licenses/1780988798_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988812_license_license.pdf b/public/uploads/licenses/1780988812_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780988844_license_license.pdf b/public/uploads/licenses/1780988844_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780989036_license_license.pdf b/public/uploads/licenses/1780989036_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780989071_license_license.pdf b/public/uploads/licenses/1780989071_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780989258_license_license.pdf b/public/uploads/licenses/1780989258_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990035_license_license.pdf b/public/uploads/licenses/1780990035_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990120_license_license.pdf b/public/uploads/licenses/1780990120_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990245_license_license.pdf b/public/uploads/licenses/1780990245_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990328_license_license.pdf b/public/uploads/licenses/1780990328_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990399_license_license.pdf b/public/uploads/licenses/1780990399_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990462_license_license.pdf b/public/uploads/licenses/1780990462_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780990748_license_license.pdf b/public/uploads/licenses/1780990748_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780991005_license_license.pdf b/public/uploads/licenses/1780991005_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780991060_license_license.pdf b/public/uploads/licenses/1780991060_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1780997733_license_fDi5IHoHP14P_1024_500.png b/public/uploads/licenses/1780997733_license_fDi5IHoHP14P_1024_500.png deleted file mode 100644 index 24d963e..0000000 Binary files a/public/uploads/licenses/1780997733_license_fDi5IHoHP14P_1024_500.png and /dev/null differ diff --git a/public/uploads/licenses/1781082884_license_license.pdf b/public/uploads/licenses/1781082884_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781082893_license_license.pdf b/public/uploads/licenses/1781082893_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781087119_license_license.pdf b/public/uploads/licenses/1781087119_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781087242_license_license.pdf b/public/uploads/licenses/1781087242_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781087905_license_license.pdf b/public/uploads/licenses/1781087905_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781087935_license_license.pdf b/public/uploads/licenses/1781087935_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781087948_license_license.pdf b/public/uploads/licenses/1781087948_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781088511_license_license.pdf b/public/uploads/licenses/1781088511_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781088528_license_license.pdf b/public/uploads/licenses/1781088528_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781088910_license_license.pdf b/public/uploads/licenses/1781088910_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781088941_license_license.pdf b/public/uploads/licenses/1781088941_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781092147_license_license.pdf b/public/uploads/licenses/1781092147_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/public/uploads/licenses/1781092407_license_license.pdf b/public/uploads/licenses/1781092407_license_license.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/resources/js/Pages/Admin/Workers/Index.jsx b/resources/js/Pages/Admin/Workers/Index.jsx index e52102d..ff9a588 100644 --- a/resources/js/Pages/Admin/Workers/Index.jsx +++ b/resources/js/Pages/Admin/Workers/Index.jsx @@ -22,7 +22,8 @@ import { Clock, Ban, FileEdit, - CheckSquare + CheckSquare, + MapPin } from 'lucide-react'; import { Table, @@ -278,123 +279,149 @@ export default function WorkerManagement({ workers }) {