# BizzCab Driver App API

Base URL: `http://127.0.0.1:5001/api`

Auth: `Authorization: Bearer <driver JWT>` except public routes.

Postman: import [BizzCab Local](postman/BizzCab.postman_environment.json), then [Driver App](postman/BizzCab-Driver-App.postman_collection.json). Run **Send OTP** then **Verify OTP**. Uploads are multipart form-data. Regenerate with `npm run postman` in `Backend`. Shared rules are in [api.md](api.md).

Screen names are the exported frames in `Bizz Cab Driver/`. Login OTP is **4 digits**. In development, `send-otp` returns `data.devOtp`.

Seeded demo driver: **Rahul Sharma**, phone `918000000006`, 3-Wheeler (Auto) `MP09AB1234`. Run `npm run seed` in `Backend`.

## Envelope

Success:

```json
{ "success": true, "message": "OTP sent", "data": {} }
```

Error:

```json
{ "success": false, "message": "Invalid OTP", "data": null }
```

`data` is `null` on logout, deactivate, and delete.

Ids are Mongo ObjectIds. File URLs are relative (`/uploads/...`); prepend the API origin.

## Screen map

| Screen | What the app does | API | Admin / seed |
|---|---|---|---|
| Splash | Logo, tagline, Driver Partner | `GET /public/app-config` | Settings. |
| Login | Mobile + Terms + DPA, Get OTP | `POST /driver/auth/send-otp`, `GET /public/cms/terms`, `GET /public/cms/data-processing` | Existing account only. |
| Verification | 4 boxes, Login, Resend OTP | `POST /driver/auth/verify-otp`, `POST /driver/auth/resend-otp` | Returns token. `nextStep` is `home`, `vehicle`, or `bank`. |
| Get Yourself Register | New mobile, Get OTP | `POST /driver/auth/register/send-otp` | Rejects numbers that already have an account. |
| Verification | 4 boxes, Continue, Resend OTP | `POST /driver/auth/register/verify-otp`, `POST /driver/auth/register/resend-otp` | Returns `signupToken`. Does not create the driver yet. |
| Create Your Account! | Name, mobile, optional email, Aadhaar/PAN, Next | `POST /driver/auth/register` | multipart. Returns token. `nextStep` is `vehicle`. |
| Vehicle Information! | Type, number, driving license, 3 photos, RC, Next | `GET /public/vehicle-types`, `POST /driver/onboarding/vehicle` | Send `name` from vehicle types as `vehicleType`. Display `dropdownLabel` (`3-Wheeler | Auto`). |
| Bank Details! | Holder, number, IFSC, bank, type, Continue | `POST /driver/onboarding/bank` | `accountType` is `savings` or `current`. `nextStep` is `home`. |
| Home | Online toggle, New/Completed/Earnings, Accept/Reject | `GET /driver/dashboard`, `PUT /driver/availability`, accept/reject | Booking `2405201030`. |
| Booking Details | Request detail | `GET /driver/rides/:id` | Base Fare, Admin Commission, Total. |
| Booking Details-1/2/3 | Completed / Rejected / Cancelled | `GET /driver/rides?status=` | Booking Management. |
| Searching for Driver | Reach banner, Arrived | `GET /driver/rides/active`, `POST .../arrived` | `statusBanner`. |
| Searching for Driver-1 | Waiting, OTP, Start, Cancel | `POST .../start`, `POST .../cancel` | `waitingLabel`. OTP not returned. |
| Searching for Driver-2 | Started, Track, Complete | `POST .../complete` | `sharePath`. |
| Ride Completed / -1 | Cash PAID | complete + `POST .../cash-collected` | `paymentStatusLabel`. |
| Ride Completed-2 | Rejected reason | reject `{ reason }` | Seeded reject reason. |
| Ride Completed-3 | Cancelled reason | cancel `{ reason }` | Seeded cancel reason. |
| Chat | Messages, call, attachment | `GET/POST /driver/rides/:rideId/messages` | `passenger.phoneRaw`. |
| My Wallet | Balance, withdraw, history | `GET /driver/wallet`, transactions, `POST /driver/payouts` | Wallets. |
| Performance | Rates vs last 7 days | `GET /driver/performance` | Driver detail. |
| Bank Details!-2 | Rate & Reviews | `GET /driver/ratings` | Ratings. |
| Notifications | Inbox | `GET /driver/notifications` | `{ items, unreadCount }`. |
| Notifications-1 | SOS | profile contacts, `sharePath`, `POST /driver/sos`, `POST /driver/reports` | SOS. |
| Profile | Menu, vehicle, logout, delete | `GET /driver/profile`, logout, `DELETE /driver/profile` | `vehicleLabel`. |
| Edit Profile | Avatar, name, phone, email, KYC | `PUT /driver/profile`, phone OTP, documents | — |

## Ride object (screens)

Ride endpoints return the stored ride plus these screen fields. The start-ride OTP is **not** returned.

```json
{
  "_id": "6aab904a84b620e14c12a7a4",
  "bookingId": "2405201030",
  "status": "searching",
  "displayStatus": "searching",
  "statusLabel": "New",
  "statusBanner": "",
  "paymentMethod": "cash",
  "paymentLabel": "Cash",
  "paymentStatus": "pending",
  "paymentStatusLabel": "UNPAID",
  "fare": 66,
  "fareDetails": {
    "baseFare": 30,
    "adminCommission": 9.9,
    "totalAmount": 56.1
  },
  "distanceKm": 3,
  "durationMin": 12,
  "distanceLabel": "03 km",
  "durationLabel": "12 min",
  "etaMinutes": 12,
  "waitingSeconds": 0,
  "waitingLabel": "",
  "otpLength": 4,
  "sharePath": "/public/rides/share/7f9f8e427a2a054e629ec9f9",
  "vehicleLabel": "3-Wheeler (Auto)",
  "passenger": {
    "id": "6aaa7f5e546d498aa3ec8a94",
    "name": "Aarav Sharma",
    "avatar": "",
    "rating": 4.9,
    "phone": "+91 ******001",
    "phoneRaw": "919000000001"
  },
  "pickup": { "address": "Vijay Nagar Square, Indore", "lat": 22.7533, "lng": 75.8937 },
  "drop": { "address": "Airport Road, Indore", "lat": 22.7218, "lng": 75.8011 },
  "rejectReason": "",
  "cancelReason": ""
}
```

| `status` | `statusLabel` | `statusBanner` |
|---|---|---|
| `searching` | New | `""` |
| `accepted` | On the way | `You will reach in 2 mins!` |
| `arrived` | You Reached! | `You Reached!` plus `waitingLabel` like `Waiting Time: 02:15 mins` |
| `ongoing` | Ride Started! | `Ride Started!` |
| `completed` | Completed | `Your Ride is Completed!` |
| `cancelled` | Cancelled | `Ride Cancelled` |
| rejected history | Rejected | `Ride Rejected` |

The same object also includes Mongo fields the app can ignore: `fareBreakdown`, `timeline`, `assignmentHistory`, `user`, `bookingCode`, `createdAt`.

Paged lists:

```json
{
  "items": [],
  "total": 6,
  "page": 1,
  "limit": 20,
  "pages": 1
}
```

---

## Public

### GET `/public/app-config`

Splash. No auth.

```json
{
  "success": true,
  "message": "App config fetched",
  "data": {
    "name": "BizzCab",
    "tagline": "Quick, comfortable, and spacious rides",
    "mobileLogo": "/uploads/logo.png",
    "email": "support@bizzcab.com",
    "contactNo": "0731-4001000",
    "address": "Vijay Nagar, Indore, Madhya Pradesh 452010",
    "website": "https://bizzcab.com",
    "sosNumbers": ["112", "100"],
    "defaultCity": "Indore",
    "rideOtpRequired": true,
    "socialLinks": {
      "facebook": "https://facebook.com/bizzcab",
      "instagram": "https://instagram.com/bizzcab",
      "twitter": "https://x.com/bizzcab",
      "youtube": "https://youtube.com/@bizzcab",
      "linkedin": "https://linkedin.com/company/bizzcab",
      "whatsapp": "917314001000"
    }
  }
}
```

### GET `/public/cms/:slug`

`terms`, `data-processing`, `privacy`.

```json
{
  "success": true,
  "message": "Page fetched",
  "data": {
    "_id": "6aaa7d506872d1eb8658dea0",
    "slug": "terms",
    "title": "Terms & Conditions",
    "content": "Use of BizzCab is subject to platform rules, fare estimates, and local regulations.",
    "updatedAt": "2026-09-17T07:02:27.290Z"
  }
}
```

### GET `/public/vehicle-types`

Vehicle Information dropdown. Use `dropdownLabel`.

```json
{
  "success": true,
  "message": "Vehicle types fetched",
  "data": [
    {
      "name": "3-Wheeler",
      "shortName": "Auto",
      "tagline": "Comfortable Ride",
      "label": "3-Wheeler (Auto)",
      "dropdownLabel": "3-Wheeler | Auto",
      "capacity": 3,
      "active": true
    }
  ]
}
```

Also seeded: `2-Wheeler | Bike`, `4-Wheeler | Cab`, `Toto | Toto`.

---

## Auth

### POST `/driver/auth/send-otp` and `/driver/auth/resend-otp`

Body: `{ "phone": "918000000006" }`

```json
{
  "success": true,
  "message": "OTP sent",
  "data": {
    "phone": "918000000006",
    "otpLength": 4,
    "expiresInSec": 300,
    "devOtp": "6436"
  }
}
```

`devOtp` is omitted in production when MSG91 is configured.

Errors: `429` wait 30 seconds, `400` invalid mobile.

### POST `/driver/auth/verify-otp`

Body: `{ "phone": "918000000006", "otp": "6436", "name": "Rahul Sharma", "email": "rahul.s@example.com", "vehicleType": "3-Wheeler" }`

Signup fields are ignored for an existing driver.

```json
{
  "success": true,
  "message": "Login successful",
  "data": {
    "token": "<jwt>",
    "driver": {
      "id": "6aab904a84b620e14c12a749",
      "name": "Rahul Sharma",
      "phone": "918000000006",
      "email": "rahul.s@example.com",
      "avatar": "",
      "approvalStatus": "approved",
      "accountStatus": "active",
      "kycStatus": "approved",
      "status": "online",
      "vehicleType": "3-Wheeler",
      "walletBalance": 2750,
      "rating": 4.8
    }
  }
}
```

New accounts come back as `approvalStatus: "pending"`, `kycStatus: "incomplete"`. Errors: `400` invalid/expired OTP, `403` deactivated/deleted.

### POST `/driver/auth/logout`

```json
{ "success": true, "message": "Logged out", "data": null }
```

---

## Profile and onboarding

### GET `/driver/profile`

```json
{
  "success": true,
  "message": "Profile fetched",
  "data": {
    "id": "6aab904a84b620e14c12a749",
    "name": "Rahul Sharma",
    "phone": "918000000006",
    "email": "rahul.s@example.com",
    "avatar": "",
    "address": "Vijay Nagar, Indore",
    "addressDetails": {
      "line1": "18, Scheme 54",
      "line2": "Near C21 Mall",
      "area": "Vijay Nagar",
      "city": "Indore",
      "state": "Madhya Pradesh",
      "pincode": "452010"
    },
    "vehicleType": "3-Wheeler",
    "vehicleLabel": "3-Wheeler (Auto)",
    "vehicle": {
      "_id": "6aab904a84b620e14c12a75d",
      "model": "Bajaj RE",
      "registrationNumber": "MP09AB1234",
      "vehicleType": "3-Wheeler",
      "vehicleLabel": "3-Wheeler (Auto)",
      "dropdownLabel": "3-Wheeler | Auto",
      "photos": [
        "/uploads/seed/rahul-photo-1.jpg",
        "/uploads/seed/rahul-photo-2.jpg",
        "/uploads/seed/rahul-photo-3.jpg"
      ],
      "rcUrl": "/uploads/seed/rahul-rc.jpg",
      "status": "approved"
    },
    "status": "online",
    "approvalStatus": "approved",
    "accountStatus": "active",
    "kycStatus": "approved",
    "documents": [
      { "type": "dl", "url": "/uploads/seed/rahul-dl.jpg", "status": "approved" },
      { "type": "aadhaar", "url": "/uploads/seed/rahul-aadhaar.jpg", "status": "approved" },
      { "type": "pan", "url": "/uploads/seed/rahul-pan.jpg", "status": "approved" }
    ],
    "bankAccounts": [
      {
        "_id": "6aab904a84b620e14c12a74f",
        "accountName": "Rahul Sharma",
        "accountNumber": "52345678901",
        "ifsc": "HDFC0001234",
        "bankName": "HDFC Bank",
        "accountType": "savings",
        "active": true
      }
    ],
    "bankDetails": {
      "accountName": "Rahul Sharma",
      "accountNumber": "52345678901",
      "ifsc": "HDFC0001234",
      "bankName": "HDFC Bank",
      "accountType": "savings"
    },
    "emergencyContacts": [{ "name": "Priya Sharma", "phone": "919876543210" }],
    "licenseNumber": "MP09 20200001234",
    "area": "Vijay Nagar",
    "rating": 4.8,
    "ratingCount": 320,
    "stats": {
      "accepted": 260,
      "rejected": 12,
      "cancelled": 8,
      "completed": 248,
      "onlineHours": 56
    },
    "walletBalance": 2750,
    "notificationSettings": { "requests": true, "promo": true, "payment": true },
    "language": "en"
  }
}
```

### PUT `/driver/profile`

JSON or multipart with `avatar`. Body: `{ "name", "email", "emergencyContacts": [{ "name", "phone" }] }`.

Response: same as GET profile, message `"Profile updated"`.

### POST `/driver/profile/phone/send-otp`

Body: `{ "phone": "918000000099" }`

Response: same shape as login send-otp (`otpLength: 4`, optional `devOtp`).

### POST `/driver/profile/phone/confirm`

Body: `{ "phone": "918000000099", "otp": "1234" }`

Response: same as GET profile, message `"Phone updated"`. Error `409` if the number is taken.

### POST `/driver/documents`

Multipart: `type=aadhaar|pan|dl|rc|insurance|puc|photo`, `file`.

Response: same as GET profile, message `"Document uploaded"`. `kycStatus` becomes `"pending"`.

### POST `/driver/vehicle`

Multipart: `vehicleType`, `registrationNumber`, optional `model`, `rc`, `dl`, `photos` (max 3) or `photo1`/`photo2`/`photo3`.

```json
{
  "success": true,
  "message": "Vehicle saved",
  "data": {
    "model": "Bajaj RE",
    "registrationNumber": "MP09AB1234",
    "vehicleType": "3-Wheeler",
    "vehicleLabel": "3-Wheeler (Auto)",
    "dropdownLabel": "3-Wheeler | Auto",
    "photos": ["/uploads/....jpg"],
    "rcUrl": "/uploads/....jpg",
    "status": "pending"
  }
}
```

### PUT `/driver/bank-account`

Body:

```json
{
  "accountName": "Rahul Sharma",
  "accountNumber": "52345678901",
  "ifsc": "HDFC0001234",
  "bankName": "HDFC Bank",
  "accountType": "savings"
}
```

`accountType` is `savings` or `current`. Response: same as GET profile, message `"Bank details saved"`.

### POST `/driver/bank-accounts`

Same body, optional `"active": true`. `201`, message `"Bank account added"`, data is the full profile.

### PUT `/driver/bank-accounts/:accountId`

Response: profile, message `"Bank account updated"`.

### PATCH `/driver/bank-accounts/:accountId/activate`

Response: profile, message `"Active bank account updated"`.

### DELETE `/driver/bank-accounts/:accountId`

Response: profile, message `"Bank account removed"`.

### POST `/driver/profile/deactivate`

```json
{ "success": true, "message": "Account deactivated", "data": null }
```

### DELETE `/driver/profile`

```json
{ "success": true, "message": "Account deleted", "data": null }
```

---

## Home

### GET `/driver/dashboard`

```json
{
  "success": true,
  "message": "Dashboard fetched",
  "data": {
    "name": "Rahul Sharma",
    "avatar": "",
    "status": "online",
    "approvalStatus": "approved",
    "kycStatus": "approved",
    "rating": 4.8,
    "ratingCount": 320,
    "walletBalance": 2750,
    "unreadCount": 2,
    "sosNumbers": ["112", "100"],
    "location": { "type": "Point", "coordinates": [75.8937, 22.7533] },
    "stats": {
      "new": 1,
      "completed": 5,
      "completedTotal": 248,
      "earnings": 288.66,
      "earningsLabel": "₹289"
    },
    "requests": []
  }
}
```

`requests` is an array of ride objects (Home New Rides cards). Seeded example uses `bookingId: "2405201030"`.

### PUT `/driver/availability`

Body: `{ "online": true, "lat": 22.7533, "lng": 75.8937 }`

```json
{
  "success": true,
  "message": "Availability updated",
  "data": { "status": "online" }
}
```

Offline: `{ "online": false }` → `"status": "offline"`. Errors: `403` if KYC is not approved or the account is not active.

### PUT `/driver/location`

Body: `{ "lat": 22.75, "lng": 75.89 }`

```json
{
  "success": true,
  "message": "Location updated",
  "data": { "type": "Point", "coordinates": [75.89, 22.75] }
}
```

Coordinates are `[lng, lat]`.

### GET `/driver/notifications`

```json
{
  "success": true,
  "message": "Notifications fetched",
  "data": {
    "items": [
      {
        "_id": "6aab904a84b620e14c12a790",
        "title": "Payment Successful",
        "body": "₹59 cash was collected for your trip to Airport Road.",
        "type": "payment",
        "read": false,
        "audience": "driver",
        "createdAt": "2026-09-17T07:01:30.948Z"
      }
    ],
    "unreadCount": 2
  }
}
```

`type` is `booking` | `promo` | `payment` | `system` | `sos`.

### PATCH `/driver/notifications/:id/read`

```json
{
  "success": true,
  "message": "Notification updated",
  "data": {
    "_id": "6aab904a84b620e14c12a790",
    "title": "Payment Successful",
    "read": true
  }
}
```

---

## Rides

### GET `/driver/requests`

```json
{
  "success": true,
  "message": "Requests fetched",
  "data": [ { "_id": "6aab904a84b620e14c12a7a4", "bookingId": "2405201030", "statusLabel": "New" } ]
}
```

Each item is a full ride object.

### GET `/driver/rides/active`

Current offered or live trip, or `null`.

```json
{ "success": true, "message": "Active ride fetched", "data": { "bookingId": "2405201030", "status": "searching" } }
```

When idle:

```json
{ "success": true, "message": "Active ride fetched", "data": null }
```

### GET `/driver/rides/:id`

Full ride object. Message `"Ride fetched"`.

### GET `/driver/rides?status=completed|rejected|cancelled`

```json
{
  "success": true,
  "message": "Ride history fetched",
  "data": {
    "items": [
      {
        "bookingId": "2405201095",
        "statusLabel": "Completed",
        "paymentLabel": "Cash",
        "paymentStatusLabel": "PAID",
        "fareDetails": { "baseFare": 30, "adminCommission": 10.26, "totalAmount": 58.14 },
        "distanceLabel": "03 km",
        "durationLabel": "12 min",
        "pickup": { "address": "Vijay Nagar Square, Indore", "lat": 22.7533, "lng": 75.8937 },
        "drop": { "address": "Airport Road, Indore", "lat": 22.7218, "lng": 75.8011 }
      }
    ],
    "total": 6,
    "page": 1,
    "limit": 20,
    "pages": 1
  }
}
```

Rejected item:

```json
{
  "bookingId": "2405201032",
  "statusLabel": "Rejected",
  "displayStatus": "rejected",
  "statusBanner": "Ride Rejected",
  "rejectReason": "Too far for me to reach on time."
}
```

Cancelled item:

```json
{
  "bookingId": "2405201031",
  "statusLabel": "Cancelled",
  "statusBanner": "Ride Cancelled",
  "cancelReason": "Passenger is not picking up call."
}
```

### POST `/driver/rides/:id/accept`

No body. Response: ride object, message `"Ride accepted"`, `status: "accepted"`, `statusBanner: "You will reach in X mins!"`. Error `409` if the offer expired.

### POST `/driver/rides/:id/reject`

Body: `{ "reason": "Too far for me to reach on time." }`

```json
{
  "success": true,
  "message": "Ride rejected",
  "data": { "reason": "Too far for me to reach on time." }
}
```

### POST `/driver/rides/:id/arrived`

Response: ride object, `status: "arrived"`, `statusBanner: "You Reached!"`, `waitingLabel` starts counting.

### POST `/driver/rides/:id/start`

Body: `{ "otp": "0204" }` (passenger’s 4-digit OTP).

Response: ride object, `status: "ongoing"`, `statusBanner: "Ride Started!"`. Error `400` if OTP is wrong.

### POST `/driver/rides/:id/complete`

Body: `{ "distanceKm": 6.2 }` optional.

Response: ride object, `status: "completed"`, `paymentStatusLabel` still `UNPAID` for unpaid cash.

### POST `/driver/rides/:id/cash-collected`

Response: ride object, `paymentStatus: "paid"`, `paymentStatusLabel: "PAID"`.

### POST `/driver/rides/:id/cancel`

Body: `{ "reason": "Passenger is not picking up call." }`

Response: ride object, `status: "cancelled"`, `cancelReason` as submitted.

---

## Wallet, payouts, performance

### GET `/driver/wallet` and GET `/driver/earnings`

Same payload.

```json
{
  "success": true,
  "message": "Earnings fetched",
  "data": {
    "today": 288.66,
    "week": 346.8,
    "month": 346.8,
    "total": 346.8,
    "commission": 61.2,
    "walletBalance": 2750,
    "rides": 6,
    "netEarnings": { "amount": 346.8, "previous": 0, "changePercent": 100, "vs": "last week" },
    "daily": { "amount": 288.66, "previous": 58.14, "changePercent": 396, "vs": "yesterday" },
    "weekly": { "amount": 346.8, "previous": 0, "changePercent": 100, "vs": "last week" },
    "monthly": { "amount": 346.8, "previous": 0, "changePercent": 100, "vs": "last month" }
  }
}
```

Home `₹2k` compact label is `dashboard.stats.earningsLabel`. Wallet balance is `walletBalance`.

### GET `/driver/wallet/transactions?type=credit|debit`

Omit `type` for All.

Credit row:

```json
{
  "success": true,
  "message": "Transactions fetched",
  "data": {
    "items": [
      {
        "_id": "6aab904a84b620e14c12a77a",
        "type": "credit",
        "amount": 2750,
        "balanceAfter": 2750,
        "reason": "admin_adjustment",
        "title": "Added Money",
        "subtitle": "Wallet Recharge",
        "createdAt": "2026-09-17T07:01:30.915Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20,
    "pages": 1
  }
}
```

Debit / commission row:

```json
{
  "type": "debit",
  "amount": 9.9,
  "reason": "commission",
  "title": "Commission Deducted",
  "subtitle": "Trip ID: 2405201033"
}
```

Payout debit title is `"Withdraw"`. Ride credit title is `"Ride Earning"`.

### POST `/driver/payouts`

Body: `{ "amount": 100, "note": "" }`

```json
{
  "success": true,
  "message": "Payout requested",
  "data": {
    "_id": "...",
    "driver": "6aab904a84b620e14c12a749",
    "amount": 100,
    "status": "pending",
    "bankAccount": {
      "accountName": "Rahul Sharma",
      "accountNumber": "52345678901",
      "ifsc": "HDFC0001234",
      "bankName": "HDFC Bank",
      "accountType": "savings"
    }
  }
}
```

`201`. Errors: `400` invalid amount or no bank account.

### GET `/driver/payouts`

```json
{ "success": true, "message": "Payouts fetched", "data": [] }
```

Each item uses `status`: `pending` | `processed` | `rejected`.

### GET `/driver/performance`

```json
{
  "success": true,
  "message": "Performance fetched",
  "data": {
    "rating": 4.8,
    "ratingCount": 320,
    "earnings": { "amount": 346.8, "changePercent": 100, "vs": "last 7 days" },
    "totalRides": { "count": 6, "changePercent": 100, "vs": "last 7 days" },
    "acceptanceRate": { "percent": 33, "changePercent": 33, "vs": "last 7 days" },
    "cancellationRate": { "percent": 17, "changePercent": 17, "vs": "last 7 days" },
    "completionRate": { "percent": 96, "changePercent": 3, "vs": "last 7 days" },
    "customerRating": { "rating": 4.3, "change": 0, "vs": "last 7 days" },
    "onlineHours": { "hours": 56, "changeHours": 56, "vs": "last 7 days" }
  }
}
```

---

## Ratings, chat, SOS, support

### GET `/driver/ratings`

```json
{
  "success": true,
  "message": "Ratings fetched",
  "data": {
    "rating": 4.8,
    "count": 320,
    "items": [
      {
        "_id": "6aab90830b862350fcf9273e",
        "stars": 4,
        "review": "Smooth ride, would book again.",
        "name": "Nisha Rao",
        "avatar": "",
        "createdAt": "2026-09-17T07:02:27.511Z"
      }
    ]
  }
}
```

### POST `/driver/ratings`

Body: `{ "rideId": "<id>", "stars": 5, "review": "Polite customer" }`

```json
{
  "success": true,
  "message": "Rating saved",
  "data": {
    "_id": "...",
    "ride": "...",
    "fromRole": "driver",
    "toRole": "user",
    "stars": 5,
    "review": "Polite customer"
  }
}
```

### GET `/driver/rides/:rideId/messages`

```json
{
  "success": true,
  "message": "Messages fetched",
  "data": {
    "passenger": {
      "_id": "6aaa7f5e546d498aa3ec8a94",
      "name": "Aarav Sharma",
      "avatar": "",
      "phone": "919000000001"
    },
    "items": [
      {
        "_id": "...",
        "fromRole": "user",
        "text": "I am near the gate",
        "attachment": "",
        "createdAt": "2026-09-17T07:01:30.000Z"
      }
    ]
  }
}
```

### POST `/driver/rides/:rideId/messages`

JSON `{ "text": "I have arrived" }` or multipart `text` + `file`.

```json
{
  "success": true,
  "message": "Message sent",
  "data": {
    "_id": "...",
    "fromRole": "driver",
    "text": "I have arrived",
    "attachment": "",
    "createdAt": "2026-09-17T07:10:00.000Z"
  }
}
```

`201`. Attachment-only is allowed (`text` can be `""`).

### POST `/driver/sos`

Body: `{ "rideId": "<id>", "lat": 22.74, "lng": 75.88, "message": "Emergency" }`

```json
{
  "success": true,
  "message": "SOS raised",
  "data": {
    "_id": "...",
    "raisedBy": "driver",
    "status": "open",
    "message": "Emergency",
    "emergencyContacts": [{ "name": "Priya Sharma", "phone": "919876543210" }],
    "sharePath": "/public/rides/share/7f9f8e427a2a054e629ec9f9"
  }
}
```

`201`. Open share URL as `{API origin}{sharePath}`.

### POST `/driver/reports`

Body: `{ "rideId", "category": "customer"|"incident", "subject": "Report customer", "message": "..." }`

```json
{
  "success": true,
  "message": "Report submitted",
  "data": {
    "_id": "...",
    "ticketCode": "SUP-2026-874521",
    "category": "customer",
    "subject": "Report customer",
    "status": "open",
    "statusLabel": "Pending"
  }
}
```

`201`.

### GET `/driver/support/faq`

```json
{
  "success": true,
  "message": "FAQ fetched",
  "data": {
    "page": {
      "slug": "faq",
      "title": "FAQ",
      "content": "Book a ride by choosing pickup, drop, and vehicle type.",
      "updatedAt": "2026-09-17T07:02:27.294Z"
    },
    "items": [
      {
        "question": "How do I go online?",
        "answer": "Complete KYC, wait for admin approval, then toggle Online on Home.",
        "sort": 10,
        "audience": "driver"
      }
    ]
  }
}
```

### GET `/driver/support/categories`

```json
{
  "success": true,
  "message": "Support categories fetched",
  "data": [
    { "slug": "lost_found", "name": "Lost & Found" },
    { "slug": "ride", "name": "Ride issue" },
    { "slug": "payment", "name": "Payment" },
    { "slug": "safety", "name": "Safety" },
    { "slug": "driver", "name": "Driver" },
    { "slug": "kyc", "name": "KYC" },
    { "slug": "other", "name": "Other" },
    { "slug": "customer", "name": "Customer" },
    { "slug": "incident", "name": "Emergency / incident" }
  ]
}
```

Use `slug` as `category` when creating a ticket.

### GET `/driver/support/tickets`

```json
{
  "success": true,
  "message": "Tickets fetched",
  "data": [
    {
      "_id": "...",
      "ticketCode": "SUP-2026-874521",
      "category": "other",
      "subject": "Need help with payout timing",
      "message": "When are weekly payouts processed?",
      "status": "resolved",
      "statusLabel": "Resolved"
    }
  ]
}
```

`open` → `statusLabel: "Pending"`. `in_progress` → `"In progress"`.

### POST `/driver/support/tickets`

JSON or multipart `image`. Body: `{ "category": "payment", "subject": "Wallet not updated", "message": "Completed ride earning missing" }`

`201`, message `"Ticket created"`, data is one ticket with `statusLabel: "Pending"`.

### GET `/driver/support/tickets/:id`

```json
{
  "success": true,
  "message": "Ticket fetched",
  "data": {
    "ticketCode": "SUP-2026-874521",
    "statusLabel": "Pending",
    "messages": [
      { "fromRole": "driver", "text": "Please check the last cash trip.", "createdAt": "2026-09-17T07:10:00.000Z" }
    ]
  }
}
```

### GET `/driver/support/tickets/:id/messages`

```json
{
  "success": true,
  "message": "Ticket messages fetched",
  "data": [{ "fromRole": "admin", "text": "Payouts are reviewed on weekdays.", "createdAt": "..." }]
}
```

### POST `/driver/support/tickets/:id/messages`

Body: `{ "text": "Please check the last cash trip." }`

`201`, message `"Message sent"`, data is the created message.

---

## Admin pages used

- Driver Management — PAN, bank account type, emergency contacts, acceptance/completion/cancellation rates
- Vehicle Management — vehicle photos, RC / insurance / PUC
- FAQs — audience All / Rider / Driver
- Wallets, payouts, SOS, Support

Run `npm run seed` in `Backend` to fill Rahul Sharma, booking `2405201030`, driver FAQs, PAN, photos, and reject/cancel reasons.
