# Auth API — payloads & responses

Base URL (local): `http://localhost:8000`
Content-Type: `application/json` for every request below.

Envelope — success is `{"ok":true,"data":{...}}`, failure is
`{"ok":false,"error":"..."}` with an optional `errors` map. **Read `error`,
not `message`.**

Rate limit: all public auth endpoints sit behind `throttle:auth` =
10 requests / minute / IP. The 11th call in a minute returns `429`; wait for
the limiter to reset before retrying.

---

## 1. Login — email ya mobile

```
POST /api/auth/login
```

The field name is `email`, but the value may be an email **or** a mobile
number. Nothing else changes for either case.

```json
{
  "email": "parth@gmail.com",
  "password": "NewPass@456"
}
```

```json
{
  "email": "8102419172",
  "password": "NewPass@456"
}
```

Mobile is matched loosely — `8102419172`, `098102419172`, `918102419172`,
`+91-81024-19172` all resolve to the same account. Email match is
case-insensitive.

| Case | Status | Response |
|---|---|---|
| Success | `200` | see below |
| Wrong password / unknown user | `401` | `{"ok":false,"error":"Invalid email/mobile or password"}` |
| `is_active = 0` | `403` | `{"ok":false,"error":"Account is disabled"}` |
| Missing identifier | `422` | `{"ok":false,"error":"Validation failed","errors":{"email":["Email ya mobile number zaroori hai."]}}` |
| Rate limited | `429` | `{"ok":false,"error":"Too Many Attempts."}` |

```json
{
  "ok": true,
  "data": {
    "token": "12|vp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "user": {
      "id": 20,
      "name": "Parth Rathod",
      "email": "parth@gmail.com",
      "role": "pandit",
      "phone": "8102419172",
      "dob": null,
      "gender": null,
      "tob": null,
      "pob": null,
      "profile_complete": false
    }
  }
}
```

Send the token as `Authorization: Bearer <token>` on every protected call.
Store it — `logout` and `change-password` need it.

---

## 2. Forgot password — step 1, send the OTP to email

```
POST /api/auth/forgot-password
```

```json
{
  "email": "parth@gmail.com"
}
```

```json
{
  "ok": true,
  "data": {
    "sent": true,
    "email": "parth@gmail.com",
    "expires": 600,
    "message": "A password reset OTP has been sent to your email. Enter it below to set a new password."
  }
}
```

The 6-digit code is emailed to the account's address; it is never returned in
the response. Valid 10 minutes, at most 5 wrong tries before the code is
burned, and at most 5 OTP requests per 10 minutes per address.

| Case | Status | Response |
|---|---|---|
| Sent | `200` | see above |
| Malformed email | `422` | `{"ok":false,"error":"Validation failed","errors":{"email":["Sahi email address daalein."]}}` |
| Email empty | `422` | `{"ok":false,"error":"Validation failed","errors":{"email":["Email zaroori hai."]}}` |
| No active account for that email | `404` | `{"ok":false,"error":"No active account found for that email. Please register first."}` |
| Too many OTP requests | `429` | `{"ok":false,"error":"Too many OTP requests. Please wait a few minutes."}` |

Requesting a new OTP invalidates any previous one — only the newest code works.

---

## 3. Forgot password — step 2, verify the OTP and set the new password

```
POST /api/auth/forgot-password/verify
```

```json
{
  "email": "parth@gmail.com",
  "code": "070087",
  "new_password": "NewPass@456",
  "new_password_confirmation": "NewPass@456"
}
```

```json
{
  "ok": true,
  "data": {
    "message": "Password updated successfully. Please log in with your new password.",
    "email": "parth@gmail.com"
  }
}
```

The OTP is consumed on success and cannot be replayed. Setting a new password
also deletes **all** of that user's Sanctum tokens, so every device is logged
out. The user must log in again with the new password.

| Case | Status | Response |
|---|---|---|
| Success | `200` | see above |
| Wrong / expired / already-used OTP | `401` | `{"ok":false,"error":"Invalid or expired OTP. Please try again."}` |
| OTP not 6 characters | `422` | `{"ok":false,"error":"Validation failed","errors":{"code":["OTP 6 digits ka hona chahiye."]}}` |
| Confirmation mismatch | `422` | `{"ok":false,"error":"Validation failed","errors":{"new_password":["Password aur confirmation match nahi karte."]}}` |
| Password shorter than 6 | `422` | `{"ok":false,"error":"Validation failed","errors":{"new_password":["..."]}}` |
| Account gone | `404` | `{"ok":false,"error":"Account not found."}` |

---

## 4. Full forgot-password flow in Postman

1. `POST /api/auth/forgot-password` with `{"email":"parth@gmail.com"}` → `200`.
2. Read the 6-digit code from the inbox of that address.
3. `POST /api/auth/forgot-password/verify` with the code plus the new password
   (twice) → `200`.
4. `POST /api/auth/login` with the **new** password → token.

---

## 5. Change password — already signed in

```
POST /api/auth/change-password
Authorization: Bearer <token>
```

```json
{
  "current_password": "OldPass@123",
  "new_password": "NewPass@456",
  "new_password_confirmation": "NewPass@456"
}
```

```json
{
  "ok": true,
  "data": { "message": "Password changed successfully." }
}
```

Other tokens are revoked; the token used for this call stays valid.

| Case | Status | Response |
|---|---|---|
| Success | `200` | see above |
| No / bad token | `401` | `{"ok":false,"error":"Unauthenticated"}` |
| Wrong current password | `422` | `{"ok":false,"error":"Your current password is incorrect."}` |
| Confirmation mismatch | `422` | `{"ok":false,"error":"Validation failed","errors":{"new_password":["..."]}}` |

---

## 6. Login by OTP

```
POST /api/auth/otp/request
```

```json
{ "identifier": "parth@gmail.com" }
```

```json
{
  "ok": true,
  "data": { "sent": true, "message": "An OTP has been sent. Enter it below to log in." }
}
```

Unknown or inactive account returns `404`
(`"No active account found for that mobile/email. Please register first."`).

```
POST /api/auth/otp/verify
```

```json
{ "identifier": "parth@gmail.com", "code": "123456" }
```

```json
{
  "ok": true,
  "data": {
    "token": "12|vp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "user": { "id": 20, "name": "Parth Rathod", "role": "pandit", "...": "..." }
  }
}
```

A bad or expired code returns `401`
(`"Invalid or expired OTP. Please try again."`). These OTPs are purpose
`login` — they cannot be used on the forgot-password endpoint, and vice versa.

---

## 7. Register, me, logout

```
POST /api/auth/register
```
```json
{
  "name": "Parth Rathod",
  "email": "parth@gmail.com",
  "phone": "8102419172",
  "password": "NewPass@456",
  "city": "Ahmedabad",
  "dob": "1995-04-12",
  "gender": "male",
  "ref": ""
}
```
`201` returns the same `{token, user}` shape as login. `email` is required and
must be a real address; an existing account yields `409`
(`"An account with this email already exists. Please log in instead."`).
`phone`, `city`, `dob`, `gender`, `ref` are optional.

```
GET /api/auth/me          Authorization: Bearer <token>
```
Returns the same `user` object as login.

```
POST /api/auth/logout     Authorization: Bearer <token>
```
```json
{ "ok": true, "data": { "message": "Logged out" } }
```

---

## Where the OTP email comes from

Delivery uses the SMTP settings in `.env`
(`MAIL_MAILER=smtp`, `MAIL_HOST=smtp.gmail.com`, `MAIL_PORT=587`,
`MAIL_USERNAME`, `MAIL_PASSWORD`, `MAIL_FROM_ADDRESS`). The message body comes
from the `password_reset_otp` row in `notif_templates`; every attempt is logged
in `notif_queue` (`status=sent` = delivered, `simulated=1` = recorded but not
sent, `status=failed` + `error` = delivery problem).

Note: the OTP email is sent for real even while the site-wide notification mode
is still in safe `log` mode — that mode only affects the other notification
types (booking, payment, WhatsApp).