You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Owner: Member 1 (Member 4 owns the Auth and Dashboard sections).
This contract is frozen at kickoff. Frontend members build against mocks shaped exactly like these examples.
Changing a field name = message the whole team first, then update this file in the same PR.
Live docs when the backend runs: http://localhost:8000/docs (Swagger, generated by FastAPI).
Conventions
Topic
Rule
Base path
/api (the frontend calls relative /api/...; Next.js rewrites proxy it to FastAPI)
Naming
snake_case everywhere: JSON fields, query params. IDs are integers named <entity>_id
Auth
httpOnly cookie ss_session (JWT, HS256, 8 h). Set by /auth/login, cleared by /auth/logout. SameSite=Lax, Secure in production
Quantities
Decimal strings in responses ("50.000"); requests accept number or string. Max 3 decimals
Dates
ISO 8601. Timestamps UTC with Z: "2026-09-26T10:15:00Z"; plain dates "2026-09-26"
// CountPriorityItem: explainable rule score (see backend/app/services/count_priority_service.py)
{ "product_id": 12, "sku": "STL001", "product_name": "Steel Rod", "uom": "kg",
"location_id": 4, "location_name": "WH/Production Floor", "quantity": "17.000",
"last_counted_at": null, "days_since_count": 3, "movements_since_count": 7, "past_discrepancies": 0,
"score": 18, "level": "low",
"reasons": ["Never counted (first movement 3 days ago)", "7 movements since last count"] }
score = 100 × (0.40 × movements since last count ÷ 20 + 0.35 × days since last count ÷ 30 + 0.25 × past count discrepancies ÷ 3), each factor capped at 1. level: high ≥ 60, medium ≥ 30, else low. Opening stock is not a discrepancy.
lines[].available = current balance at the source location (delivery/transfer/adjustment) or at the destination (receipt), so the form can warn early. The server re-checks on validate anyway.
INSUFFICIENT_STOCK lists every short line in details.lines (line_id, product_id, sku, available, requested) so the UI can highlight them all at once.
PATCH rules: omitted fields are kept; sending lines replaces all lines.
Validating an already-done operation returns 200 with "already_done": true and an empty stock_effects. No second stock change (safe for double-clicks and retries).
The dashboard's operations table reuses GET /api/operations with type / status / warehouse_id / location_id / category_id filters (the PDF's "dynamic filters").
pending_* / scheduled_transfers = operations of that type in draft, waiting or ready.
Method
Path
Access
Query
Returns
GET
/api/dashboard/activity
auth
days (1–90, default 14), warehouse_id
{ days: [{ date, receipt, delivery, transfer, adjustment }], pipeline: { draft, waiting, ready } }: validated operations per day (UTC) and open operations by stage, for the dashboard charts
low_stock_items lists every product below its reorder point, out-of-stock first, then low; each carries suggested_order (up to max_qty). The dashboard's Reorder button turns one into a draft receipt, and Record count on a Count-next item creates a draft adjustment for that product-location.