# BeanOpsGate API — desktop contract

Base URL (prod): `https://beanops.bbtl.app/api/v1`

**Read-only.** Every endpoint only SELECTs from `bbtl_workspace`. Auth is a stateless
token (the user id encrypted with the app's `APP_KEY`) — there is no session and no
token table, so nothing is ever written to the workspace DB. Login and task fetch need
internet; the desktop app caches the results and runs the timer offline.

All responses are JSON and share the envelope `{ "success": bool, ... }`.

---

## 1. Login
`POST /auth/login`

```json
{ "email": "jay@brainbean.us", "password": "secret" }
```

**200**
```json
{
  "success": true,
  "data": {
    "token": "eyJpdiI6...",              // store this; send as Bearer on later calls
    "user": {
      "id": 1,
      "name": "Jay BrainBean",
      "email": "jay@brainbean.us",
      "employee_id": "EMP001",
      "designation": "Engineer",
      "mobile": "5551234",
      "is_team_lead": false,
      "roles": ["Employee"],              // a user may have several
      "role": "Employee"                  // primary role to display
    }
  }
}
```
**401** — `{ "success": false, "message": "...", "errors": { "email": ["Invalid email or password."] } }`

Bad credentials trigger one silent repair before that 401 is returned. The local
`users` row is a mirror of the workspace refreshed by hand from the admin
screen, so a new hire — or anyone who changed their workspace password since the
last sync — would otherwise be locked out until someone pressed *Sync Workspace
Users*. Worklance re-reads that one address from the workspace (10s ceiling, one
read per address per minute) and checks the password again.

Nothing about the contract changes: the response is identical either way, and a
workspace that is slow, unreachable, switched off or simply doesn't know the
address just leaves the 401 in place. A **native** account is never touched by
this — its password has nothing to do with the workspace. Clients should be
aware only that a rejected login can take a few seconds longer than it used to.

## 2. Current user (refresh profile)
`GET /auth/me`  ·  header `Authorization: Bearer <token>`
→ `{ "success": true, "data": { "user": { ...same shape as above... } } }`
**401** if the token is missing/expired/tampered.

## 3. Assigned tasks (timer dropdown)
`GET /tasks`  ·  header `Authorization: Bearer <token>`

Returns the signed-in user's **own** assigned tasks that are still workable
(excludes `completed`/`cancelled` and soft-deleted), ordered by priority (P0→P4)
then due date.

**200**
```json
{
  "success": true,
  "data": {
    "tasks": [
      { "id": 2, "title": "Mockups", "type": "Design", "status": "assigned",
        "priority": "P0", "client_id": 5, "due_date": null },
      { "id": 1, "title": "Build login", "type": "Development", "status": "in_progress",
        "priority": "P1", "client_id": 5, "due_date": "2026-07-01T12:00:00+00:00" }
    ]
  }
}
```

---

## Desktop flow (2 pages)
1. **Login page** → `POST /auth/login`. On success, store `token` + `user` locally
   (so the app keeps working offline). Show the user's name + `role`.
2. **Timer board** → `GET /tasks` to fill the task dropdown. User picks one of their
   tasks and starts a **client-side** timer. (Starting/stopping the timer is local —
   it does not call the API and never writes to the workspace.)

Token lifetime is 30 days (`App\Support\DesktopToken::TTL_DAYS`); on a 401 from
`/auth/me` or `/tasks`, send the user back through login while online.

## Notes / next
- `client_id` is returned as an id; if the board needs client names, we can add a
  read-only `clients` lookup or join it into `/tasks`.
- Statuses in the workspace: `assigned, in_progress, in_review, in_qa, on_hold,
  submitted, completed, cancelled`. Terminal set (hidden from the dropdown) is
  `completed, cancelled` — adjust in `App\Models\Workspace\Task::TERMINAL_STATUSES`.
