docs(auth,devices): Beam WO5 batch 3 — 15 endpoints backfilled, registry pinned

Beam backfilled auth core (login/register/refresh/logout/profile) and devices
core (list/update/delete/shelf CRUD/sidecar/register flow) with frontmatter,
provenance, and labeled examples. All claimed shapes verified against
handlers: AuthResponse {access_token, refresh_token omitempty, token_type,
expires_in, user}, update device {device_updated, device}, shelf wrappers,
DeviceAuthStatusResponse.

Claude registry verification: 15 entries pinned by type (LoginRequest,
RegisterRequest, AuthResponse, RefreshTokenRequest/Response, UserProfile,
DeviceListResponse, DeviceUpdateRequest, AddToShelfRequest, SidecarConfig,
DeviceRegistrationRequest/Response, DeviceAuthStatusRequest/Response).
Removed revoke_device.md — name-only duplicate of delete_device.md (same
DELETE /api/devices/:id route; was the last audit-orphan doc).

Gate catches this round (subtle Go serialization facts, now pinned in docs):
- uuid.UUID arrays never serialize as omitted: register/status response
  ALWAYS carries device_id (all-zeros while pending) despite omitempty
- update device request binds device_name, not name
- register response includes device_id alongside registration_id
- list devices response is a {devices, total} wrapper
This commit is contained in:
John O'Keefe committed 2026-10-24 15:48:18 -04:00
1 parent eacc33bfac
commit ae9588f1c6
17 files changed
+278 -103

No files matched your search

+23 -10
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/devices/:id/shelves
auth: jwt
area: devices
---
<!-- source: internal/handlers/media.go:256 MediaHandler.AddToShelf -->
# Add to Shelf
Add a media item to a device's shelf (Kobo reading shelf).
@@ -14,25 +22,30 @@ Add a media item to a device's shelf (Kobo reading shelf).
## Request Body
| Field | Type | Required | Description |
| ------------- | ------------- | -------- | ------------------------------- |
| media_item_id | string (UUID) | Yes | Media item UUID to add to shelf |
| Field | Type | Required | Description |
| -------------- | --------------- | -------- | ------------------------------- |
| media_item_ids | array of string | Yes | Media item UUIDs to add to shelf |
| shelf_name | string | No | Shelf name (default "Default") |
| shelf_position | int | No | Starting shelf position |
### Example Request
```json
```json example-request
{
"media_item_id": "550e8400-e29b-41d4-a716-446655440001"
"media_item_ids": ["550e8400-e29b-41d4-a716-446655440001"],
"shelf_name": "Default",
"shelf_position": 0
}
```
## Response (201 Created)
## Response (200 OK)
```json
```json example-response
{
"message": "Item added to shelf successfully",
"device_id": "uuid",
"media_item_id": "uuid"
"status": "success",
"added_count": 1,
"shelf_name": "Default",
"total_count": 1
}
```
+15 -2
View File
@@ -1,3 +1,11 @@
---
method: DELETE
path: /api/devices/:id/shelves/clear
auth: jwt
area: devices
---
<!-- source: internal/handlers/media.go:413 MediaHandler.ClearShelf -->
# Clear Shelf
Remove all items from a device's shelf.
@@ -24,9 +32,14 @@ DELETE /api/devices/550e8400-e29b-41d4-a716-446655440000/shelves/clear
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
## Response (204 No Content)
## Response (200 OK)
Shelf cleared successfully.
```json example-response
{
"status": "success",
"message": "all shelves cleared"
}
```
## Error Responses
@@ -1,3 +1,11 @@
---
method: DELETE
path: /api/devices/:id
auth: jwt
area: devices
---
<!-- source: internal/handlers/devices.go:413 DeviceHandler.DeleteDevice -->
# Delete Device
Delete a device and revoke its access.
+17 -13
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/devices/register/status
auth: none
area: devices
---
<!-- source: internal/handlers/devices.go:178 DeviceHandler.CheckRegistrationStatus -->
# Get Device / Check Registration Status
Check device registration status or get device details.
@@ -14,7 +22,7 @@ Check device registration status or get device details.
### Example Request (Status Check)
```json
```json example-request
{
"registration_id": "registration-uuid"
}
@@ -22,22 +30,18 @@ Check device registration status or get device details.
## Response (200 OK)
```json
```json example-response
{
"status": "pending|approved",
"message": "awaiting user approval",
"expires_in": 123,
"auth_token": "device-bearer-token...",
"device_id": "uuid",
"sync_endpoints": {
"progress": "https://bookhoard.com/api/sync/progress",
"metadata": "https://bookhoard.com/api/sync/metadata",
"annotations": "https://bookhoard.com/api/sync/annotations"
}
"status": "pending",
"device_id": "00000000-0000-0000-0000-000000000000"
}
```
`status` is `pending` or `approved`. While pending, the response includes `message` and `expires_in` (seconds remaining). Once approved, the response includes `auth_token`, `device_id`, and `sync_endpoints`; `auth_token` fields are empty when pending.
`device_id` is always present (a uuid.UUID array never serializes as omitted —
it reads as all-zeros while pending). `status` is `pending` or `approved`.
While pending, the response includes `message` and `expires_in` (seconds
remaining). Once approved, the response includes `auth_token`, `device_id`,
and `sync_endpoints`; `auth_token` fields are empty when pending.
**The approved response is single-use**: the registration is deleted from the pending map once returned, so store the `auth_token` immediately. A repeat status check for the same `registration_id` returns 404.
+19 -4
View File
@@ -1,3 +1,11 @@
---
method: GET
path: /api/devices/:id/shelves
auth: jwt
area: devices
---
<!-- source: internal/handlers/media.go:301 MediaHandler.GetShelf -->
# Get Shelf
Get all items on a device's shelf.
@@ -26,15 +34,22 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
## Response (200 OK)
```json
```json example-response
{
"items": [
"status": "success",
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"shelf_name": "",
"books": [
{
"id": "uuid",
"media_item_id": "uuid",
"title": "Book Title",
"authors": ["Author Name"],
"cover_url": "/api/media-items/uuid/cover",
"author": "Author Name",
"file_path": "",
"mime_type": "",
"shelf_name": "",
"shelf_position": 0,
"revision_number": 0,
"added_at": "2026-02-08T10:00:00Z"
}
],
@@ -1,3 +1,11 @@
---
method: GET
path: /api/devices/:id/sidecar
auth: jwt
area: devices
---
<!-- source: internal/handlers/sidecar.go:67 SidecarHandler.GetSidecarConfig -->
# Get Device Sidecar Config
Returns the KOReader/Kobo sidecar configuration (`.bookhoard.json`) for a device: server endpoints, the user's books (keyed by SHA-256 with UUID fallback), and collections. Used by the Bookhoard KOReader plugin to self-configure after approval.
@@ -7,7 +15,7 @@ Returns the KOReader/Kobo sidecar configuration (`.bookhoard.json`) for a device
### Response (200 OK)
```json
```json example-response
{
"version": "1",
"bookhoard": {
+11 -2
View File
@@ -1,3 +1,11 @@
---
method: GET
path: /api/devices
auth: jwt
area: devices
---
<!-- source: internal/handlers/devices.go:259 DeviceHandler.ListDevices -->
# List User Devices
Retrieve all devices registered to the current user.
@@ -20,7 +28,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
## Response (200 OK)
```json
```json example-response
{
"devices": [
{
@@ -33,7 +41,8 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
"auto_sync": true,
"sync_frequency_minutes": 5
}
]
],
"total": 1
}
```
+11 -2
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/devices/register
auth: none
area: devices
---
<!-- source: internal/handlers/devices.go:123 DeviceHandler.InitiateRegistration -->
# Register Device
Register a new device for sync.
@@ -16,7 +24,7 @@ Register a new device for sync.
### Example Request
```json
```json example-request
{
"device_name": "My Kobo Clara",
"device_type": "kobo",
@@ -26,8 +34,9 @@ Register a new device for sync.
## Response (201 Created)
```json
```json example-response
{
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"registration_id": "registration-uuid",
"auth_url": "https://bookhoard.com/devices/approve/abc123",
"qr_code": "data:image/png;base64,iVBORw0KG...",
@@ -1,3 +1,11 @@
---
method: DELETE
path: /api/devices/:id/shelves
auth: jwt
area: devices
---
<!-- source: internal/handlers/media.go:384 MediaHandler.RemoveFromShelf -->
# Remove from Shelf
Remove a media item from a device's shelf.
@@ -26,9 +34,14 @@ Remove a media item from a device's shelf.
}
```
## Response (204 No Content)
## Response (200 OK)
Item removed from shelf successfully.
```json example-response
{
"status": "success",
"message": "book removed from shelf"
}
```
## Error Responses
@@ -1,37 +0,0 @@
# Revoke Device
Revoke access to a device.
**Endpoint**: `DELETE /api/devices/{device_id}`
**Auth**: Required
## Path Parameters
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------- |
| device_id | string | Yes | Device UUID |
## Request Headers
| Header | Type | Required | Description |
| ------------- | ------ | -------- | ------------ |
| Authorization | string | Yes | Bearer token |
### Example Request
```http
DELETE /api/devices/uuid
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
## Response (204 No Content)
Device revoked successfully.
## Error Responses
| Code | Description |
| ---- | ----------------------------- |
| 401 | Invalid or expired token |
| 403 | User does not own this device |
| 404 | Device not found |
+27 -11
View File
@@ -1,3 +1,11 @@
---
method: PUT
path: /api/devices/:id
auth: jwt
area: devices
---
<!-- source: internal/handlers/devices.go:336 DeviceHandler.UpdateDevice -->
# Update Device
Update a device's information.
@@ -21,24 +29,32 @@ Update a device's information.
### Example Request
```json
```json example-request
{
"name": "My Kobo Clara",
"device_type": "kobo"
"device_name": "My Kobo Clara",
"sync_enabled": true,
"auto_sync": true,
"sync_frequency_minutes": 60
}
```
## Response (200 OK)
```json
```json example-response
{
"id": "uuid",
"name": "My Kobo Clara",
"device_type": "kobo",
"user_id": "user-uuid",
"status": "active",
"created_at": "2026-02-08T10:00:00Z",
"updated_at": "2026-02-08T11:00:00Z"
"device_updated": true,
"device": {
"id": "uuid",
"device_name": "My Kobo Clara",
"device_type": "kobo",
"last_sync": null,
"last_seen": null,
"sync_enabled": true,
"auto_sync": true,
"sync_frequency_minutes": 5,
"created_at": "2026-02-08T10:00:00Z",
"auth_token": ""
}
}
```