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:
1 parent
eacc33bfac
commit
ae9588f1c6
17 files changed
+278
-103
No files matched your search
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: POST
|
||||
path: /api/auth/login
|
||||
auth: none
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:378 AuthHandler.Login -->
|
||||
|
||||
# Login User
|
||||
|
||||
Authenticate with email and password.
|
||||
@@ -15,7 +23,7 @@ Authenticate with email and password.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"login": "user@example.com",
|
||||
"password": "SecureP@ss123!"
|
||||
@@ -24,10 +32,9 @@ Authenticate with email and password.
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "d4f5g6h7...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 604800,
|
||||
"user": {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: POST
|
||||
path: /api/auth/logout
|
||||
auth: none
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/refresh_token.go:76 AuthHandler.Logout -->
|
||||
|
||||
# Logout User
|
||||
|
||||
Invalidate the current JWT token.
|
||||
@@ -19,9 +27,13 @@ POST /api/auth/logout
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
## Response (204 No Content)
|
||||
## Response (200 OK)
|
||||
|
||||
No response body.
|
||||
```json example-response
|
||||
{
|
||||
"message": "logged out successfully"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: POST
|
||||
path: /api/auth/refresh
|
||||
auth: none
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/refresh_token.go:36 AuthHandler.RefreshAccessToken -->
|
||||
|
||||
# Refresh Token
|
||||
|
||||
Obtain a new JWT access token using a refresh token.
|
||||
@@ -14,7 +22,7 @@ Obtain a new JWT access token using a refresh token.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"refresh_token": "d4f5g6h7..."
|
||||
}
|
||||
@@ -22,7 +30,7 @@ Obtain a new JWT access token using a refresh token.
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"access_token": "new-jwt-token",
|
||||
"token_type": "Bearer",
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: POST
|
||||
path: /api/auth/register
|
||||
auth: none
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:153 AuthHandler.Register -->
|
||||
|
||||
# Register User
|
||||
|
||||
Create a new user account.
|
||||
@@ -18,7 +26,7 @@ Create a new user account.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"username": "john",
|
||||
@@ -30,10 +38,9 @@ Create a new user account.
|
||||
|
||||
## Response (201 Created)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "d4f5g6h7...",
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 604800,
|
||||
"user": {
|
||||
@@ -42,9 +49,7 @@ Create a new user account.
|
||||
"username": "john",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"role": "user",
|
||||
"theme": "tokyo-night",
|
||||
"created_at": "2026-01-31T10:00:00Z"
|
||||
"role": "user"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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 |
|
||||
@@ -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": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/auth/profile
|
||||
auth: jwt
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:530 AuthHandler.GetProfile -->
|
||||
|
||||
# Get Current User Profile
|
||||
|
||||
Retrieve the current authenticated user's profile.
|
||||
@@ -20,17 +28,14 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"username": "john",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"theme": "tokyo-night",
|
||||
"role": "user",
|
||||
"max_devices": 10,
|
||||
"created_at": "2026-01-31T10:00:00Z"
|
||||
"role": "user"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user