The platform admin's last shell-only jobs are endpoints
Suspend or reinstate a company (PATCH /api/admin/clients/{id}), reset
its owner's password (shown once), and delete it - and an owner can
remove a shop opened by mistake (DELETE /api/sites/{site}, empty only).
Suspension ends every session the company holds in the same
transaction: login and ingest already refused an inactive client, but a
live access token would have kept reading for up to twelve hours, so
'suspend' would have meant 'suspend some time tomorrow'. Deletion is
deliberately two steps - the company must already be suspended and the
request repeats the slug - because the data under it is biometric.
Face images go first (a storage failure aborts with nothing touched),
then the broker logins, then the rows by cascade.
Exercised against the local Postgres and broker: create, open a shop,
remove it (two plugin commands), refuse delete while active, suspend
(owner's token 401 immediately), reset, delete, zero rows left.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
60
API.md
60
API.md
@@ -49,13 +49,13 @@ user; the tenant is always taken from the session and never from the request.
|
||||
| `DELETE /api/visitors/{id}` — erasure | manager |
|
||||
| `GET /api/sites` · `GET /api/sites/{site}/check` · `GET /api/cameras` · `GET /api/cameras/{id}/snapshot.jpg` · `GET /api/cameras/{id}/live` | authed |
|
||||
| `POST /api/sites/{site}/cameras` · `PATCH` / `DELETE /api/cameras/{id}` · `POST /api/cameras/{id}/check` | manager |
|
||||
| `POST /api/sites` — open a shop | owner |
|
||||
| `POST /api/sites` — open a shop · `DELETE /api/sites/{site}` — remove an empty one | owner |
|
||||
| `POST /api/sites/{site}/enrolment-code` | manager |
|
||||
| `GET /api/team` | authed (tenant users only) |
|
||||
| `POST /api/team/members` · `POST /api/team/{id}/password` · `PATCH /api/team/{id}` · `/api/team/invitations*` | manager |
|
||||
| `GET /api/reports/footfall` · `GET /api/reports/conversion` | authed |
|
||||
| `POST /api/assistant` | authed |
|
||||
| `GET` / `POST /api/admin/clients` | **platform admin** |
|
||||
| `GET` / `POST /api/admin/clients` · `PATCH /api/admin/clients/{id}` · `POST /api/admin/clients/{id}/owner-password` · `DELETE /api/admin/clients/{id}` | **platform admin** |
|
||||
| `/api/agent/*` | **shop PC token** — never a user |
|
||||
|
||||
A role that may not call something gets **403 `forbidden`** with a message
|
||||
@@ -102,6 +102,9 @@ travels over chat.
|
||||
|
||||
```
|
||||
GET /api/admin/clients → every merchant, with site and user counts
|
||||
PATCH /api/admin/clients/{id} {"active": false} → suspend (or true: reinstate)
|
||||
POST /api/admin/clients/{id}/owner-password → new owner password, shown once
|
||||
DELETE /api/admin/clients/{id} {"confirm": "<slug>"} → delete a SUSPENDED company
|
||||
```
|
||||
|
||||
### Tier 2 — the merchant owner registers sales staff
|
||||
@@ -218,9 +221,10 @@ somebody else's account.
|
||||
|
||||
- **There is no mobile app in this repository.** Tier 3 is a complete API with
|
||||
no client yet. Everything above is what that app will call.
|
||||
- **The admin cannot reset a merchant owner's password over HTTP**, nor suspend
|
||||
or delete a merchant. Today that is `behavision-server provision` on the
|
||||
server.
|
||||
- **A shop with visit history cannot be deleted**, only its cameras removed.
|
||||
`DELETE /api/sites/{site}` is for the shop opened by mistake (no visits, no
|
||||
cameras); taking away footfall and faces is an erasure decision, and there
|
||||
is no endpoint for it yet.
|
||||
|
||||
---
|
||||
|
||||
@@ -261,10 +265,13 @@ GET /api/reports/footfall?from=&to= → the numbers, with their confidenc
|
||||
POST /api/auth/login (an account with no company)
|
||||
GET /api/admin/clients → every company
|
||||
POST /api/admin/clients → create one, with its owner
|
||||
PATCH /api/admin/clients/{id} → suspend / reinstate
|
||||
POST /api/admin/clients/{id}/owner-password → reset the owner's password
|
||||
DELETE /api/admin/clients/{id} → delete, once suspended
|
||||
```
|
||||
|
||||
That is the whole admin surface today. Everything inside a company is the
|
||||
company's own business and is reached by signing in as one of its users.
|
||||
That is the whole admin surface. Everything inside a company is the company's
|
||||
own business and is reached by signing in as one of its users.
|
||||
|
||||
---
|
||||
|
||||
@@ -1045,6 +1052,45 @@ in as the company's owner, or by `behavision-server provision` on the server.
|
||||
|
||||
---
|
||||
|
||||
### `PATCH /api/admin/clients/{id}` — suspend or reinstate
|
||||
|
||||
```
|
||||
{ "active": false }
|
||||
→ 200 { "client": { "id": "…", "slug": "acme", "active": false, "sites": 2, "users": 5, … },
|
||||
"sessions_revoked": 3 }
|
||||
```
|
||||
|
||||
Suspension is complete the moment it returns: the company's users cannot sign
|
||||
in, every session they hold is revoked in the same transaction (so a live
|
||||
access token stops working now, not at expiry), and visits from its shop PCs
|
||||
are dropped at ingest. `{"active": true}` reinstates; sessions are not
|
||||
restored — people sign in again.
|
||||
|
||||
### `POST /api/admin/clients/{id}/owner-password` — reset the owner's password
|
||||
|
||||
```
|
||||
{ "email": "owner@acme.com" } ← optional when the company has exactly one owner
|
||||
→ 200 { "email": "owner@acme.com", "password": "n7xw…" } ← shown ONCE
|
||||
```
|
||||
|
||||
For the owner who has locked themselves out with nobody above them. Generated,
|
||||
never chosen; every session that owner held is revoked. With several owners
|
||||
and no `email`, 400 listing them.
|
||||
|
||||
### `DELETE /api/admin/clients/{id}` — delete a company
|
||||
|
||||
```
|
||||
{ "confirm": "acme" }
|
||||
→ 200 { "deleted": "acme", "images_deleted": 12 }
|
||||
```
|
||||
|
||||
Irreversible, and the data is biometric, so it is a two-step decision: the
|
||||
company must already be **suspended** (`409 still_active` otherwise) and the
|
||||
body must repeat its slug. Stored face images are deleted from object storage
|
||||
first — a failure there is `502 storage_error` and nothing else is touched —
|
||||
then the shop PCs' broker logins, then every row (templates, visits, users,
|
||||
sessions, cameras) by cascade.
|
||||
|
||||
## 12. Errors
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user