{"openapi":"3.0.3","info":{"title":"AKAL API Documentation","description":"Official OpenAPI documentation for AKAL Mobile Client APIs. Every response is HTTP 200 \u2014 branch on the `code` field inside the payload, never the HTTP status. Requires the `x-platform` and `x-app-version` headers.","version":"1.2.0","contact":{"name":"AKAL Engineering Team","email":"support@appringer.co.in"}},"servers":[{"url":"https:\/\/akal.appringer.co.in\/api\/v1","description":"Production Server"},{"url":"http:\/\/127.0.0.1:8000\/api\/v1","description":"Local Development Server"}],"tags":[{"name":"Auth","description":"OTP request, verification, registration and session lifecycle"},{"name":"Master Data & System Config","description":"Bootstrap configuration and client-safe system config"},{"name":"Profile","description":"Profile, live location and per-user settings"},{"name":"Emergencies","description":"SOS lifecycle: create, discover, accept, resolve"},{"name":"Chat","description":"Group conversation and member management"},{"name":"Safety & Moderation","description":"Abuse reporting and member safety"},{"name":"Notifications","description":"In-app notification feed"},{"name":"Media","description":"Hybrid local + S3 media storage"}],"paths":{"\/auth\/otp\/request":{"post":{"tags":["Auth"],"summary":"Request Login OTP","description":"Sends a 6-digit OTP code to the provided phone number.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform (android or ios)"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phone_number"],"properties":{"phone_number":{"type":"string","example":"+919599110203","description":"User mobile phone number with country code"}}}}}},"responses":{"200":{"description":"OTP dispatched successfully or active cooldown error response.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"message":{"type":"string","example":"OTP sent successfully."},"data":{"type":"object","properties":{"cooldown_seconds":{"type":"integer","example":60}}}}}}}}}}},"\/auth\/otp\/verify":{"post":{"tags":["Auth"],"summary":"Verify OTP & Authenticate","description":"Verifies 6-digit OTP code and returns Bearer authentication token.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform (android or ios)"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phone_number","otp"],"properties":{"phone_number":{"type":"string","example":"+919599110203"},"otp":{"type":"string","example":"123456"},"device_detail":{"type":"object","properties":{"device_name":{"type":"string","example":"iPhone 15 Pro"},"os_version":{"type":"string","example":"iOS 17.4"},"app_version":{"type":"string","example":"1.0.0"}}}}}}}},"responses":{"200":{"description":"Authentication successful.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"message":{"type":"string","example":"Login successful."},"data":{"type":"object","properties":{"token":{"type":"string","example":"1|sanctum_token_string"},"user":{"type":"object","properties":{"id":{"type":"integer","example":1},"phone_number":{"type":"string","example":"+919599110203"}}}}}}}}}}}}},"\/masterData":{"get":{"tags":["Master Data & System Config"],"summary":"Get Public Master Data","description":"App bootstrap payload: version gate, support info and the emergency category catalogue. Categories come from here so they change without a store release.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform (android or ios)"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Master data retrieved successfully.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"data":{"type":"object","properties":{"app_name":{"type":"string","example":"AKAL Mobile"},"android_version":{"type":"string","example":"1.0.0"},"ios_version":{"type":"string","example":"1.0.0"}}}}}}}}}}},"\/system\/config":{"get":{"tags":["Master Data & System Config"],"summary":"Get Authenticated Client Config","security":[{"bearerAuth":[]}],"parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform (android or ios)"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Client-safe configuration.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"data":{"type":"object","properties":{"support_number":{"type":"string","example":"+919599110203"},"help_url":{"type":"string","example":"https:\/\/akal.app\/help"}}}}}}}}},"description":"Client-safe configuration for authenticated app users. Server secrets (SMS gateway key, FCM server key, third-party server tokens) are never returned here \u2014 every mobile user holds a Sanctum token. Secrets are managed in the admin panel."}},"\/auth\/otp\/resend":{"post":{"tags":["Auth"],"summary":"Resend Login OTP","description":"Re-sends the OTP. Subject to the same 30-second cooldown as the initial request.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"OTP sent successfully via SMS."},"data":{"type":"object","properties":{"cooldown_seconds":{"type":"integer","example":30}}}}}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phone_number"],"properties":{"phone_number":{"type":"string","example":"+919599110203"}}}}}}}},"\/auth\/register":{"post":{"tags":["Auth"],"summary":"Complete Registration","description":"Called after OTP verification returns `next_action: register`. Fills the profile of the auto-created user.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Registration completed successfully."},"data":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"full_name":{"type":"string","example":"John Doe"},"phone_number":{"type":"string","example":"+919599110203"},"avatar_url":{"type":"string","nullable":true},"medical_info":{"type":"string","nullable":true},"is_online":{"type":"boolean"},"responder_available":{"type":"boolean"},"auto_sos_on_startup":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}}}}}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["full_name"],"properties":{"full_name":{"type":"string","example":"John Doe"},"medical_info":{"type":"string","nullable":true}}}}}}}},"\/auth\/logout":{"post":{"tags":["Auth"],"summary":"Logout","description":"Revokes the current access token only. Other signed-in devices are unaffected.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Logged out successfully."},"data":{"type":"object","nullable":true}}}}}}},"security":[{"bearerAuth":[]}]}},"\/auth\/account":{"delete":{"tags":["Auth"],"summary":"Delete Account","description":"Soft-deletes the account, revokes every token and releases the phone number. The same number registering again receives a NEW user id with no inherited history.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Account deleted successfully."},"data":{"type":"object","nullable":true}}}}}}},"security":[{"bearerAuth":[]}]}},"\/users\/me":{"get":{"tags":["Profile"],"summary":"Get Profile","description":"Authenticated user's profile.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Profile retrieved successfully."},"data":{"type":"object","properties":{"id":{"type":"string"},"full_name":{"type":"string","example":"John Doe"},"phone_number":{"type":"string","example":"+919599110203"},"avatar_url":{"type":"string","nullable":true},"medical_info":{"type":"string","nullable":true},"is_online":{"type":"boolean"},"responder_available":{"type":"boolean"},"auto_sos_on_startup":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}}}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Profile"],"summary":"Update Profile","description":"Partial update \u2014 only supplied fields are written. `medical_info` is encrypted at rest.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Profile updated successfully."},"data":{"type":"object","properties":{"id":{"type":"string"},"full_name":{"type":"string","example":"John Doe"},"phone_number":{"type":"string","example":"+919599110203"},"avatar_url":{"type":"string","nullable":true},"medical_info":{"type":"string","nullable":true},"is_online":{"type":"boolean"},"responder_available":{"type":"boolean"},"auto_sos_on_startup":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}}}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"full_name":{"type":"string"},"medical_info":{"type":"string","nullable":true},"avatar_url":{"type":"string","nullable":true},"auto_sos_on_startup":{"type":"boolean"},"responder_available":{"type":"boolean"}}}}}}}},"\/users\/location":{"post":{"tags":["Profile"],"summary":"Update Live Location","description":"Coarse last-known position used to target SOS broadcasts. Called on app open and after ~100 m of movement. Live tracking during an emergency runs over the socket, not here.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Location updated successfully."},"data":{"type":"object","nullable":true}}}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["lat","lng"],"properties":{"lat":{"type":"number","example":21.1702},"lng":{"type":"number","example":72.8311}}}}}}}},"\/users\/me\/settings":{"get":{"tags":["Profile"],"summary":"Get Settings","description":"Everything behind the Settings screen.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Settings retrieved successfully."},"data":{"type":"object","properties":{"emergency_type":{"type":"string","nullable":true,"example":"medical"},"notification_radius_km":{"type":"number","example":5},"push_notifications_enabled":{"type":"boolean"},"emergency_alerts_enabled":{"type":"boolean"},"location_sharing_enabled":{"type":"boolean"},"online_status_visible":{"type":"boolean"},"share_contacts_during_sos":{"type":"boolean"},"share_location_during_sos":{"type":"boolean"},"voice_note_during_sos":{"type":"boolean"}}}}}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Profile"],"summary":"Update Settings","description":"Partial update. Untouched keys keep their value.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Settings updated successfully."},"data":{"type":"object","properties":{"emergency_type":{"type":"string","nullable":true,"example":"medical"},"notification_radius_km":{"type":"number","example":5},"push_notifications_enabled":{"type":"boolean"},"emergency_alerts_enabled":{"type":"boolean"},"location_sharing_enabled":{"type":"boolean"},"online_status_visible":{"type":"boolean"},"share_contacts_during_sos":{"type":"boolean"},"share_location_during_sos":{"type":"boolean"},"voice_note_during_sos":{"type":"boolean"}}}}}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"emergency_type":{"type":"string","nullable":true,"example":"medical"},"notification_radius_km":{"type":"number","example":5},"push_notifications_enabled":{"type":"boolean"},"emergency_alerts_enabled":{"type":"boolean"},"location_sharing_enabled":{"type":"boolean"},"online_status_visible":{"type":"boolean"},"share_contacts_during_sos":{"type":"boolean"},"share_location_during_sos":{"type":"boolean"},"voice_note_during_sos":{"type":"boolean"}}}}}}}},"\/users\/me\/contacts":{"get":{"tags":["Emergency Contacts"],"summary":"Get Emergency Contacts","description":"Fetch list of active emergency contacts for authenticated user.","responses":{"200":{"description":"Emergency contacts retrieved successfully."}}},"post":{"tags":["Emergency Contacts"],"summary":"Add Emergency Contact","description":"Add a new trusted emergency contact.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["name","phone_number"],"properties":{"name":{"type":"string","example":"Dr. Sharma"},"phone_number":{"type":"string","example":"+919876543210"},"relationship":{"type":"string","example":"Doctor"},"is_primary":{"type":"boolean","example":true},"notify_on_sos":{"type":"boolean","example":true}}}}}},"responses":{"200":{"description":"Emergency contact added successfully."}}}},"\/users\/me\/contacts\/{id}":{"put":{"tags":["Emergency Contacts"],"summary":"Update Emergency Contact","description":"Update an existing emergency contact.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"phone_number":{"type":"string"},"relationship":{"type":"string"},"is_primary":{"type":"boolean"},"notify_on_sos":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Emergency contact updated successfully."}}},"delete":{"tags":["Emergency Contacts"],"summary":"Delete Emergency Contact","description":"Soft delete an emergency contact.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Emergency contact deleted successfully."}}}},"\/emergencies\/nearby":{"get":{"tags":["Emergencies"],"summary":"Get Nearby Emergencies","description":"Live emergencies around a point, closest first. `distance_m` is computed server-side so every client sorts identically.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"lat","in":"query","required":true,"schema":{"type":"number","example":21.1702},"description":"Caller latitude"},{"name":"lng","in":"query","required":true,"schema":{"type":"number","example":72.8311},"description":"Caller longitude"},{"name":"radius_km","in":"query","required":false,"schema":{"type":"number","example":5},"description":"Search radius, default 5"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","example":20},"description":"Max results, default 20"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Nearby emergencies retrieved successfully."},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"01JBQ8F3K2M7X9YV4T6N1PZ0AC"},"category":{"type":"object","properties":{"slug":{"type":"string","example":"medical"},"label":{"type":"string","example":"Medical Help"},"icon":{"type":"string"},"color_hex":{"type":"string","example":"#E11D48"}}},"note":{"type":"string","nullable":true},"requester_id":{"type":"string"},"requester_name":{"type":"string"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"distance_m":{"type":"integer","example":840},"status":{"type":"string","enum":["searching","active","resolved","cancelled","expired"]},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"},"chat_room_id":{"type":"string","nullable":true},"radius_km":{"type":"number"},"expires_at":{"type":"string","format":"date-time"},"timestamp":{"type":"string","format":"date-time"}}}},"next_cursor":{"type":"integer","nullable":true},"total":{"type":"integer"}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/emergencies":{"post":{"tags":["Emergencies"],"summary":"Create Emergency (Trigger SOS)","description":"Persists the SOS and queues responder fan-out on the `high` queue. The response never waits on delivery. Business code `402` when the free SOS quota is exhausted and no subscription is active.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Emergency broadcast started."},"data":{"type":"object","properties":{"id":{"type":"string","example":"01JBQ8F3K2M7X9YV4T6N1PZ0AC"},"category":{"type":"object","properties":{"slug":{"type":"string","example":"medical"},"label":{"type":"string","example":"Medical Help"},"icon":{"type":"string"},"color_hex":{"type":"string","example":"#E11D48"}}},"note":{"type":"string","nullable":true},"requester_id":{"type":"string"},"requester_name":{"type":"string"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"distance_m":{"type":"integer","example":840},"status":{"type":"string","enum":["searching","active","resolved","cancelled","expired"]},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"},"chat_room_id":{"type":"string","nullable":true},"radius_km":{"type":"number"},"expires_at":{"type":"string","format":"date-time"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["category","latitude","longitude"],"properties":{"category":{"type":"string","example":"medical","description":"Slug from masterData.emergency_categories"},"note":{"type":"string","nullable":true,"example":"Chest pain, conscious"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"radius_km":{"type":"number","nullable":true,"example":5},"media_id":{"type":"string","nullable":true,"description":"public_id returned by POST \/uploads"}}}}}}}},"\/emergencies\/{publicId}":{"get":{"tags":["Emergencies"],"summary":"Get Emergency","description":"Rehydrates the active-SOS screen after a cold start or socket drop.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"publicId","in":"path","required":true,"schema":{"type":"string"},"description":"Emergency public id (ULID)"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Emergency retrieved successfully."},"data":{"type":"object","properties":{"id":{"type":"string","example":"01JBQ8F3K2M7X9YV4T6N1PZ0AC"},"category":{"type":"object","properties":{"slug":{"type":"string","example":"medical"},"label":{"type":"string","example":"Medical Help"},"icon":{"type":"string"},"color_hex":{"type":"string","example":"#E11D48"}}},"note":{"type":"string","nullable":true},"requester_id":{"type":"string"},"requester_name":{"type":"string"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"distance_m":{"type":"integer","example":840},"status":{"type":"string","enum":["searching","active","resolved","cancelled","expired"]},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"},"chat_room_id":{"type":"string","nullable":true},"radius_km":{"type":"number"},"expires_at":{"type":"string","format":"date-time"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/emergencies\/{publicId}\/accept":{"post":{"tags":["Emergencies"],"summary":"Accept Emergency","description":"Row-locked assignment. Business codes: `409` capacity full or already accepted, `410` no longer active, `422` own emergency.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"publicId","in":"path","required":true,"schema":{"type":"string"},"description":"Emergency public id (ULID)"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Assigned successfully."},"data":{"type":"object","properties":{"emergency_id":{"type":"string"},"chat_room_id":{"type":"string"},"responder_position":{"type":"integer","example":2},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/emergencies\/{publicId}\/decline":{"post":{"tags":["Emergencies"],"summary":"Decline Emergency","description":"Records the decline so escalation knows this responder is not coming.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"publicId","in":"path","required":true,"schema":{"type":"string"},"description":"Emergency public id (ULID)"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Emergency declined."},"data":{"type":"object","nullable":true}}}}}}},"security":[{"bearerAuth":[]}]}},"\/emergencies\/{publicId}\/resolve":{"post":{"tags":["Emergencies"],"summary":"Resolve or Cancel Emergency","description":"Requester only \u2014 `403` for anyone else. An SOS cancelled within the refund window without any acceptance does not consume the free quota.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"publicId","in":"path","required":true,"schema":{"type":"string"},"description":"Emergency public id (ULID)"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Emergency resolved successfully."},"data":{"type":"object","properties":{"id":{"type":"string","example":"01JBQ8F3K2M7X9YV4T6N1PZ0AC"},"category":{"type":"object","properties":{"slug":{"type":"string","example":"medical"},"label":{"type":"string","example":"Medical Help"},"icon":{"type":"string"},"color_hex":{"type":"string","example":"#E11D48"}}},"note":{"type":"string","nullable":true},"requester_id":{"type":"string"},"requester_name":{"type":"string"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"distance_m":{"type":"integer","example":840},"status":{"type":"string","enum":["searching","active","resolved","cancelled","expired"]},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"},"chat_room_id":{"type":"string","nullable":true},"radius_km":{"type":"number"},"expires_at":{"type":"string","format":"date-time"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/emergencies\/history":{"get":{"tags":["Emergencies"],"summary":"Emergency History","description":"The caller's past emergencies. Keyset pagination \u2014 pass the returned `next_cursor`, never an offset.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"status","in":"query","required":false,"schema":{"type":"string","example":"resolved"},"description":"Filter by status"},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer"},"description":"Id cursor from the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","example":20},"description":"Page size, default 20"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Emergency history retrieved successfully."},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"01JBQ8F3K2M7X9YV4T6N1PZ0AC"},"category":{"type":"object","properties":{"slug":{"type":"string","example":"medical"},"label":{"type":"string","example":"Medical Help"},"icon":{"type":"string"},"color_hex":{"type":"string","example":"#E11D48"}}},"note":{"type":"string","nullable":true},"requester_id":{"type":"string"},"requester_name":{"type":"string"},"latitude":{"type":"number","example":21.1702},"longitude":{"type":"number","example":72.8311},"distance_m":{"type":"integer","example":840},"status":{"type":"string","enum":["searching","active","resolved","cancelled","expired"]},"responder_count":{"type":"integer"},"max_responders":{"type":"integer"},"chat_room_id":{"type":"string","nullable":true},"radius_km":{"type":"number"},"expires_at":{"type":"string","format":"date-time"},"timestamp":{"type":"string","format":"date-time"}}}},"next_cursor":{"type":"integer","nullable":true}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/chats\/{roomId}\/messages":{"get":{"tags":["Chat"],"summary":"Get Message History","description":"Keyset paginated, newest first. `403` when the caller is not an active participant of the room.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"roomId","in":"path","required":true,"schema":{"type":"string"},"description":"Chat room public id (ULID)"},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer"},"description":"Id cursor from the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","example":30},"description":"Page size, default 30"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Messages retrieved successfully."},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender_id":{"type":"string"},"sender_name":{"type":"string"},"text":{"type":"string","nullable":true},"message_type":{"type":"string","enum":["text","image","audio","video","location","system"]},"media_url":{"type":"string","nullable":true},"duration":{"type":"integer","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"timestamp":{"type":"string","format":"date-time"}}}},"next_cursor":{"type":"integer","nullable":true}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/notifications":{"get":{"tags":["Notifications"],"summary":"Get Notifications","description":"In-app notification feed for the caller, newest first.","parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer"},"description":"Id cursor from the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","example":20},"description":"Page size, default 20"}],"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"Notifications retrieved successfully."},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"category":{"type":"string","enum":["emergency","chat","system"]},"route_payload":{"type":"object","nullable":true},"is_read":{"type":"boolean"},"timestamp":{"type":"string","format":"date-time"}}}},"next_cursor":{"type":"integer","nullable":true},"unread_count":{"type":"integer","example":2}}}}}}}}},"security":[{"bearerAuth":[]}]}},"\/uploads":{"post":{"tags":["Media"],"summary":"Upload File","security":[{"bearerAuth":[]}],"parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"}],"description":"multipart\/form-data. Lands on local disk and returns immediately; S3 replication runs on a queue. Type is validated from the file's own bytes, not the client Content-Type header.","requestBody":{"required":true,"content":{"multipart\/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"context":{"type":"string","enum":["avatar","emergency","chat","voice_note"],"default":"chat"}}}}}},"responses":{"200":{"description":"Standard AKAL envelope. HTTP is always 200; branch on `code`.","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"code":{"type":"integer","example":200},"message":{"type":"string","example":"File uploaded successfully."},"data":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"type":{"type":"string","enum":["image","audio","video","document"]},"size_bytes":{"type":"integer"},"duration":{"type":"integer","nullable":true}}}}}}}}}}},"\/media\/{publicId}":{"get":{"tags":["Media"],"summary":"Fetch Media","security":[{"bearerAuth":[]}],"parameters":[{"name":"x-platform","in":"header","required":true,"schema":{"type":"string","enum":["android","ios"],"default":"android"},"description":"Mobile client platform"},{"name":"x-app-version","in":"header","required":true,"schema":{"type":"string","default":"1.0.0"},"description":"Mobile client app version"},{"name":"publicId","in":"path","required":true,"schema":{"type":"string"},"description":"Media asset public id (ULID)"}],"description":"Read-through fetch: served from the local cache when present, otherwise pulled from S3, re-cached locally and streamed.","responses":{"200":{"description":"Binary file stream.","content":{"application\/octet-stream":{"schema":{"type":"string","format":"binary"}}}}}}},"\/realtime\/socket-events":{"get":{"tags":["WebSockets & Realtime"],"summary":"Realtime WebSockets Specification & Channels","description":"WebSocket Connection & Event Payload Reference.\n\n**WebSocket URL**: `wss:\/\/akal.appringer.co.in\/app\/akal-reverb-key` (Pusher \/ Laravel Reverb Protocol)\n\n### Channels & Event Specs:\n1. **`presence-emergency.{emergency_id}`**:\n   - Event: `ResponderLocationUpdated`\n   - Payload: `{ \"responder_id\": 5, \"latitude\": 30.7333, \"longitude\": 76.7794, \"updated_at\": \"2026-08-12T10:45:00Z\" }`\n   - Event: `EmergencyStatusChanged`\n   - Payload: `{ \"emergency_id\": 1, \"status\": \"resolved\", \"responder_count\": 2 }`\n\n2. **`private-chat.room.{chat_room_public_id}`**:\n   - Event: `ChatMessageSent`\n   - Payload: `{ \"id\": \"msg_123\", \"sender_id\": 5, \"text\": \"Help is on the way!\", \"created_at\": \"2026-08-12T10:45:00Z\" }`\n\n3. **`private-user.{user_id}.notifications`**:\n   - Event: `NotificationFeedCreated`\n   - Payload: `{ \"title\": \"Responder Nearby\", \"body\": \"Responder John is 200m away.\", \"unread_count\": 3 }`","responses":{"200":{"description":"WebSocket Specification Information","content":{"application\/json":{"schema":{"type":"object","properties":{"websocket_host":{"type":"string","example":"wss:\/\/akal.appringer.co.in\/app\/akal-reverb-key"},"protocol":{"type":"string","example":"Pusher \/ Laravel Reverb 1.0"},"channels":{"type":"array","items":{"type":"string"},"example":["presence-emergency.{emergency_id}","private-chat.room.{chat_room_public_id}","private-user.{user_id}.notifications"]}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Sanctum"}}}}