Dashboard — Backend API Gaps
Date: 2026-05-29
Author: Frontend team (Project Monet — apps/dashboard)
Recipient: Malcolm / Fibretrace Backend team
Source of truth: audit.json + README.md in this directory
Context
apps/dashboard is a fork of the Lovable.dev project fibre-trace-studio. Malcolm built the
Lovable prototype using Supabase as a rapid-delivery scratchpad. The frontend team has ported the
UI screen-by-screen and is routing every data call through an adapter layer (apiAdapter.ts) that
translates Lovable’s supabase.from(table) calls to Fibretrace Laravel REST endpoints where they
exist.
After an exhaustive cross-reference against the full canvas API inventory (~300 endpoints, 6 route files) and the SAT codebase, 14 feature groups have no Laravel equivalent. For each of these, the Lovable Supabase schema is the design spec — Malcolm already defined the shape for the MVP. This document hands those specs to BE so the team can implement them in Laravel. Priority is based on user-visible impact and how many dashboard pages are blocked.
Priority Summary Table
| # | Feature / Table group | Dashboard consumers | Why no endpoint exists | Suggested endpoint shape |
|---|---|---|---|---|
| 1 | sessions | CollectionDetail, SessionDetail, useVerifications |
Canvas GET /orders/:code/processes has a total field mismatch — missing session_code, conducted_at, fibre_detected, facility_id |
GET /sessions (company-scoped list) + GET /sessions/:id |
| 2 | verifications | CollectionDetail, AddVerificationsModal, CollectionPreview, VerificationPreview |
Canvas orders/processes model has no “verification grouping” resource | GET /orders/:order_code/verifications + CRUD mutations |
| 3 | audits — per-scan rows | SessionDetail, AdminVerifications, useVerifications |
GET /user/audits returns process-level containers, not per-scan ft_id/result/notes rows |
GET /sessions/:id/audits |
| 4 | dashboardApi stats | Home, ProducerDashboard |
Canvas dashboardApi.ts uses inconsistent base URL /api/dashboard — appears to be a scaffold; hooks may not map to real endpoints |
GET /api/dashboard/stats etc. — BE to confirm or define |
| 5 | companies — confidence fields | useVerifications, useProgrammeHeatmap, useConfidence |
Canvas GET /public-companies returns only uid + name |
Add fields to GET /public-companies OR new GET /company/:uid/confidence |
| 6 | session_shares | CollectionDetail, SessionDetail |
Canvas has POST /user/audits/:uid/send (one-way transfer) but no many-to-many share join |
POST /sessions/:id/shares + DELETE /sessions/:id/shares/:share_id |
| 7 | fibre_programmes (+ participants, producers, producer_facilities, reservations) | AdminProgrammes, useProgrammes, useProgrammeHeatmap |
Entirely new BE domain — no /programs or /fibre-programmes endpoint anywhere in canvas |
BE to define — /fibre-programmes CRUD + nested relationships |
| 8 | pigment_ids (+ pigment_orders, pigment_company_assignments) | AdminPigmentIds, useProgrammes, SessionDetail |
Lovable-native fibre tracking tables — zero canvas equivalent | BE to define — /pigment-ids CRUD |
| 9 | production_records (+ production_claims, claim_sessions, production_files) | FibreCreation, LogProductionModal, AdminFibreProduction |
No /production-records or /production-claims in any canvas inventory file |
BE to define — /production-records CRUD |
| 10 | sliver_delivery_units (+ sdu_activity_logs) | SDU, AdminSDU, useGuideSignals |
Canvas only has scanning_intensity_module flag in company metadata — no /sdu endpoint |
BE to define — /sliver-delivery-units CRUD |
| 11 | evidence_requests (+ evidence_request_shares) | EvidencePack, EvidenceRequestDetail, useEvidenceRequests |
Lovable-native evidence pack workflow — no /evidence-requests in canvas |
BE to define — /evidence-requests CRUD |
| 12 | notification_preferences — mutations | SettingsNotifications, useNotificationPrefs |
GET /service-tab covers template reads; no per-user preference upsert endpoint in canvas |
Already covered by PUT /api/v2/user (notification_emails field) — FE will handle, no new BE work needed here unless per-preference granularity is wanted |
| 13 | company_details | AdminCompanies |
Canvas has POST /company-meta but different semantics — no direct company_details CRUD |
POST /companies, PUT /companies/:id, DELETE /companies/:id |
| 14 | company_users — mutations | AdminUsers, AdminCompanies |
Canvas has useSetUserStatusMutation / useDeleteUserMutation but different semantics — no admin user-create |
POST /company-users (admin create) + confirm semantics of existing status/delete endpoints |
| 15 | facility_licenses | AdminFacilities, AdminLicensing |
Admin-only licensing concept — no /facility-licenses in canvas |
BE to define — /facility-licenses CRUD |
| 16 | onboarding_tasks | AdminOnboarding |
Admin onboarding wizard — Lovable-only concept, no canvas equivalent | BE to define — /onboarding-tasks CRUD |
Per-Gap Detail
1. sessions
Supabase tables: sessions
Why blocked: Canvas GET /orders/:orderCode/processes returns ProcessProcesList:
{ id, due_date, scan_count, auditor: {id, name}, manufacturer_process: {id, name} }
This is a total field mismatch — missing session_code, conducted_at, fibre_detected,
facility_id, pigment_id, verification_id, auditor_name. Cannot map without fabrication.
Dashboard pages / hooks consuming this:
CollectionDetail.tsx— session list within a collectionSessionDetail.tsx— full session objectuseVerifications.ts— session aggregation per collectionuseGuideSignals.ts— facility-scoped session signals
Lovable Supabase shape (columns the dashboard reads):
sessions (
id uuid PRIMARY KEY,
session_code text NOT NULL,
conducted_at timestamptz NOT NULL,
fibre_detected text, -- e.g. "15/20 scans detected"
scan_count int DEFAULT 0,
facility_id uuid REFERENCES facilities(id),
verification_id uuid REFERENCES verifications(id),
production_record_id uuid REFERENCES production_records(id) NULLABLE,
auditor_name text,
pigment_id uuid NULLABLE,
deleted_at timestamptz NULLABLE
)
Queries the dashboard performs:
// List — CollectionDetail.tsx
supabase.from("sessions")
.select("id, session_code, conducted_at, fibre_detected, scan_count, facility_id, verification_id, production_record_id, auditor_name, pigment_id")
.in("facility_id", myFacilityIds)
.is("deleted_at", null)
// Detail — SessionDetail.tsx
supabase.from("sessions").select("*").eq("id", sessionId).single()
// Delete — CollectionDetail.tsx
supabase.from("sessions").delete().eq("id", sessionId)
Proposed REST endpoints (follow canvas orders/ conventions):
GET /sessions
Query params: facility_id?, verification_id?, page?, number_per_page?, sort?, sort_by?
Response: { data: Session[], meta: { total, current_page, ... } }
GET /sessions/:id
Response: { data: Session }
DELETE /sessions/:id
Response: 204
Note: BE team should decide: does “session” map to an enriched order process record, or is it
a new concept? If the former, enrich GET /orders/:orderCode/processes response with the missing
fields. If the latter, expose /sessions as a new resource.
2. verifications (groupings)
Supabase tables: verifications
Why blocked: Canvas treats an “order process” as the unit of work. Lovable introduces “verifications” as a named grouping that links sessions to a collection (order). There is no equivalent resource in any canvas endpoint.
Dashboard pages / hooks consuming this:
CollectionDetail.tsx,CreateCollectionFlow.tsx— list/create/reassign verificationsAddVerificationsModal.tsx— bulk assign verifications to a collectionCollectionPreview.tsx,VerificationPreview.tsx— read-only previewuseVerifications.ts,useCollections.ts
Lovable Supabase shape:
verifications (
id uuid PRIMARY KEY,
name text NOT NULL,
status text, -- e.g. "active", "completed"
collection_id uuid REFERENCES collections(id), -- maps to order_code
deleted_at timestamptz NULLABLE
)
Queries the dashboard performs:
supabase.from("verifications")
.select("id, name, status, collection_id")
.in("collection_id", collectionIds)
.is("deleted_at", null)
// Bulk reassign (AddVerificationsModal)
supabase.from("verifications")
.update({ collection_id: newCollectionId })
.in("id", verificationIds)
Proposed REST endpoints:
GET /orders/:order_code/verifications
Response: { data: [{id, name, status, collection_id}] }
POST /orders/:order_code/verifications
Body: { name: string }
Response: { data: Verification }
PUT /orders/:order_code/verifications/:id
Body: { name?, status?, collection_id? }
Response: { data: Verification }
DELETE /orders/:order_code/verifications/:id
Response: 204
3. audits — per-scan rows
Supabase tables: audits
Why blocked (PARTIAL): GET /api/v1/sat-audits exists but returns one row per SAT
audit submission with an embedded scans[] array. Lovable’s dashboard treats audits as
per-scan rows with individual ft_id, blockchain_guid, result, notes, and a session_id
FK. Canvas GET /user/audits returns process-level audit containers for the authenticated
auditor’s assignment queue — wrong scope and shape.
The sat-audits endpoint is also listed as “partially implemented” in BE’s own notes. Please
confirm whether it is live and production-ready before FE wires the dashboard.
Dashboard pages / hooks consuming this:
SessionDetail.tsx— per-scan rows within a sessionAdminVerifications.tsx— admin audit list with soft-delete / edituseVerifications.ts—audit_countper session + PO reference extraction
Lovable Supabase shape:
audits (
id uuid PRIMARY KEY,
ft_id text NULLABLE, -- FibreTrace ID detected in this scan
blockchain_guid text NULLABLE,
result text, -- "pass" | "fail" | "pending"
notes text NULLABLE, -- contains "PO: XXX" pattern
session_id uuid REFERENCES sessions(id),
created_at timestamptz,
deleted_at timestamptz NULLABLE -- soft delete
)
Queries the dashboard performs:
// Per-session scan list — SessionDetail.tsx
supabase.from("audits")
.select("id, ft_id, blockchain_guid, result, notes, created_at")
.eq("session_id", sessionId)
// Audit count + PO extraction — useVerifications.ts
supabase.from("audits")
.select("id, session_id, notes")
.in("session_id", sessionIds)
.is("deleted_at", null)
// Admin mutations — AdminVerifications.tsx
supabase.from("audits").update({ deleted_at: now }).eq("id", id) // soft-delete
supabase.from("audits").update({ deleted_at: null }).eq("id", id) // restore
supabase.from("audits").update({ result, notes }).eq("id", id) // edit
Proposed REST endpoints:
GET /sessions/:id/audits
Response: { data: [{id, ft_id, blockchain_guid, result, notes, session_id, created_at}] }
PATCH /sat-audits/:id
Body: { result?, notes?, deleted_at? }
Response: { data: Audit }
Note: If BE confirms that a SAT audit submission = a “session” and the scans[] items =
“audit rows”, then the FE can denormalise the response client-side. BE should confirm the mapping
explicitly so FE can finalise the adapter.
4. dashboardApi stats
Why blocked: Canvas dashboardApi.ts uses baseUrl: '/api/dashboard' with a Bearer token
pulled directly from Redux state — inconsistent with all other canvas APIs that use the standard
base URL. Canvas team notes indicate this file may be a scaffold/stub. The four hook names
(useGetDashboardDataQuery, useGetDashboardStatsQuery, useGetRecentActivityQuery,
useGetChartDataQuery) may not correspond to real endpoints.
Dashboard pages consuming this:
Home.tsx— summary stats (total scans, active collections, recent activity)ProducerDashboard.tsx— producer-view stats
FE needs BE to either:
- Confirm the
/api/dashboardendpoints are real and document request/response shapes; or - Define new aggregate stats endpoints (e.g.
GET /dashboard/stats) that return the home-page metrics, and FE will wire them.
Minimum data Home.tsx needs (based on Lovable UI):
{
"total_scans": 1234,
"active_collections": 56,
"scanners_assigned": 7,
"recent_activity": [
{ "id": "...", "type": "scan|collection|session", "description": "...", "created_at": "..." }
]
}
5. companies — confidence fields
Supabase tables: companies (additional columns)
Why blocked (PARTIAL): GET /public-companies exists and is wired. It returns only uid
and name. The dashboard confidence UI (ConfidenceBadge, ConfidenceCard, useConfidence)
reads confidence_score and confidence_rating per company — these are Lovable-only Supabase
columns with zero BE source today.
Dashboard consumers: useVerifications.ts:72, useProgrammeHeatmap.ts:139, useConfidence.ts
Lovable Supabase shape (additional columns on companies):
confidence_score numeric(5,2) NULLABLE, -- 0-100 computed score
confidence_rating text NULLABLE, -- "Moderate" | "Good" | "Excellent"
confidence_summary text,
confidence_drivers jsonb, -- array of score driver objects
confidence_last_calculated_at timestamptz NULLABLE,
confidence_manual_override boolean DEFAULT false,
confidence_override_reason text,
confidence_updated_by text, -- user uid
confidence_notes text,
sharing_enabled boolean DEFAULT false
Proposed approach (BE to choose one):
Option A — Add fields to existing GET /public-companies response:
GET /public-companies
Response: { data: [{ uid, name, confidence_score, confidence_rating, sharing_enabled }] }
Option B — New endpoint per company:
GET /company/:uid/confidence
Response: { data: { confidence_score, confidence_rating, confidence_summary,
confidence_drivers, confidence_last_calculated_at,
confidence_manual_override, confidence_override_reason,
confidence_notes, sharing_enabled } }
6. session_shares
Supabase tables: session_shares
Why blocked: Canvas has POST /user/audits/:uid/send (one-way audit transfer), but Lovable
models session sharing as a many-to-many join — a company can receive read access to another
company’s session. The semantics differ; BE decision required.
Dashboard consumers: CollectionDetail.tsx:211, SessionDetail.tsx
Lovable Supabase shape:
session_shares (
id uuid PRIMARY KEY,
session_id uuid REFERENCES sessions(id),
shared_with uuid REFERENCES companies(id), -- receiving company
shared_by uuid REFERENCES companies(id), -- sharing company
created_at timestamptz
)
Proposed REST endpoints:
GET /sessions/:id/shares
Response: { data: [{id, session_id, shared_with, shared_by, created_at}] }
POST /sessions/:id/shares
Body: { shared_with: string } -- company uid
Response: { data: SessionShare }
DELETE /sessions/:id/shares/:share_id
Response: 204
7. fibre_programmes (and nested tables)
Supabase tables: fibre_programmes, programme_participants, programme_producers,
programme_producer_facilities, programme_reservations
Why blocked: Entirely new BE domain. No /programs or /fibre-programmes endpoint exists in
any of the 6 canvas inventory files. The MAPPERS["fibre_programmes"] block in apiAdapter.ts
already has a // TODO Phase D comment pointing here.
Dashboard consumers: AdminProgrammes.tsx, useProgrammes.ts, useProgrammeHeatmap.ts
Lovable Supabase shapes (key columns):
fibre_programmes (
id uuid PRIMARY KEY,
name text NOT NULL,
description text,
status text, -- "active" | "draft" | "completed"
owner_id uuid REFERENCES companies(id),
start_date date NULLABLE,
end_date date NULLABLE,
created_at timestamptz
)
programme_participants (
id uuid PRIMARY KEY,
programme_id uuid REFERENCES fibre_programmes(id),
company_id uuid REFERENCES companies(id),
role text, -- "producer" | "verifier" | "auditor"
joined_at timestamptz
)
programme_producers (
id uuid PRIMARY KEY,
programme_id uuid REFERENCES fibre_programmes(id),
company_id uuid REFERENCES companies(id)
)
programme_producer_facilities (
id uuid PRIMARY KEY,
producer_id uuid REFERENCES programme_producers(id),
facility_id uuid REFERENCES facilities(id)
)
programme_reservations (
id uuid PRIMARY KEY,
programme_id uuid REFERENCES fibre_programmes(id),
quantity int,
reserved_by uuid REFERENCES companies(id),
reserved_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /fibre-programmes -- company-scoped list
POST /fibre-programmes -- create
GET /fibre-programmes/:id -- detail with nested participants/producers
PUT /fibre-programmes/:id -- update
GET /fibre-programmes/:id/participants
POST /fibre-programmes/:id/participants
GET /fibre-programmes/:id/producers
8. pigment_ids (and related tables)
Supabase tables: pigment_ids, pigment_orders, pigment_company_assignments
Why blocked: Lovable-native fibre/pigment tracking tables. Zero canvas equivalent found in any inventory file.
Dashboard consumers: AdminPigmentIds.tsx, useProgrammes.ts, useProgrammeHeatmap.ts,
SessionDetail.tsx:134
Lovable Supabase shapes (key columns):
pigment_ids (
id uuid PRIMARY KEY,
code text NOT NULL UNIQUE,
description text NULLABLE,
status text, -- "available" | "assigned" | "depleted"
created_at timestamptz
)
pigment_orders (
id uuid PRIMARY KEY,
pigment_id uuid REFERENCES pigment_ids(id),
quantity int,
ordered_by uuid REFERENCES companies(id),
ordered_at timestamptz
)
pigment_company_assignments (
id uuid PRIMARY KEY,
pigment_id uuid REFERENCES pigment_ids(id),
company_id uuid REFERENCES companies(id),
assigned_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /pigment-ids
POST /pigment-ids
GET /pigment-ids/:id
PUT /pigment-ids/:id
POST /pigment-ids/:id/assign body: { company_id }
GET /pigment-orders
POST /pigment-orders
9. production_records (and related tables)
Supabase tables: production_records, production_claims, production_claim_sessions,
production_files
Why blocked: Fibre production / SDU claim flow. No /production-records or
/production-claims endpoint in any canvas inventory file.
Dashboard consumers: FibreCreation.tsx, LogProductionModal.tsx, useGuideSignals.ts,
usePersona.ts, AdminFibreProduction.tsx
Lovable Supabase shapes (key columns):
production_records (
id uuid PRIMARY KEY,
company_id uuid REFERENCES companies(id),
facility_id uuid REFERENCES facilities(id),
quantity numeric,
unit text, -- "kg" | "m" | etc.
batch_number text NULLABLE,
produced_at timestamptz,
created_at timestamptz
)
production_claims (
id uuid PRIMARY KEY,
production_record_id uuid REFERENCES production_records(id),
claimed_by uuid REFERENCES companies(id),
quantity_claimed numeric,
status text, -- "pending" | "approved" | "rejected"
claimed_at timestamptz
)
production_claim_sessions (
id uuid PRIMARY KEY,
claim_id uuid REFERENCES production_claims(id),
session_id uuid REFERENCES sessions(id)
)
production_files (
id uuid PRIMARY KEY,
production_record_id uuid REFERENCES production_records(id),
file_url text,
file_type text,
uploaded_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /production-records
POST /production-records
GET /production-records/:id
GET /production-claims
POST /production-claims body: { production_record_id, quantity_claimed }
PUT /production-claims/:id body: { status: "approved" | "rejected" }
10. sliver_delivery_units (SDU)
Supabase tables: sliver_delivery_units, sdu_activity_logs
Why blocked: Canvas only stores a scanning_intensity_module boolean flag in company
metadata. There is no /sliver-delivery-units or /sdu endpoint anywhere in the canvas
inventory.
Dashboard consumers: SDU.tsx, AdminSDU.tsx, useGuideSignals.ts
Lovable Supabase shapes (key columns):
sliver_delivery_units (
id uuid PRIMARY KEY,
company_id uuid REFERENCES companies(id),
sdu_code text NOT NULL UNIQUE,
status text, -- "active" | "inactive" | "deployed"
facility_id uuid REFERENCES facilities(id) NULLABLE,
created_at timestamptz
)
sdu_activity_logs (
id uuid PRIMARY KEY,
sdu_id uuid REFERENCES sliver_delivery_units(id),
action text, -- "created" | "deployed" | "recalled"
performed_by uuid REFERENCES users(id),
performed_at timestamptz,
notes text NULLABLE
)
Minimum endpoints needed (BE to define full spec):
GET /sliver-delivery-units
POST /sliver-delivery-units
GET /sliver-delivery-units/:id
PUT /sliver-delivery-units/:id
GET /sliver-delivery-units/:id/activity-logs
11. evidence_requests
Supabase tables: evidence_requests, evidence_request_shares
Why blocked: Lovable-native evidence pack workflow. No /evidence-requests endpoint in any
canvas inventory file.
Dashboard consumers: useEvidenceRequests.ts, EvidencePack.tsx, EvidenceRequestDetail.tsx
Lovable Supabase shapes (key columns):
evidence_requests (
id uuid PRIMARY KEY,
requester_id uuid REFERENCES companies(id),
target_company_id uuid REFERENCES companies(id),
status text, -- "pending" | "fulfilled" | "rejected"
message text NULLABLE,
created_at timestamptz,
fulfilled_at timestamptz NULLABLE
)
evidence_request_shares (
id uuid PRIMARY KEY,
evidence_request_id uuid REFERENCES evidence_requests(id),
session_id uuid REFERENCES sessions(id),
shared_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /evidence-requests -- scoped to auth company (as requester or target)
POST /evidence-requests body: { target_company_id, message? }
GET /evidence-requests/:id
PUT /evidence-requests/:id body: { status: "fulfilled" | "rejected" }
POST /evidence-requests/:id/shares body: { session_id }
12. notification_preferences — mutations
Why this is likely already handled:
GET /user already returns notification_emails: Record<OPT_IN_KEY, boolean> on the user
object (confirmed in both canvas userApi.ts:55-60 and SAT auth.ts:27).
PUT /api/v2/user (canvas userApi.ts:70-74) accepts the full user object spread with
notification_emails overridden — this is the write path for toggling notification preferences.
FE assessment: No new endpoint is needed. FE will wire PUT /api/v2/user directly.
The only ask: Confirm that PUT /api/v2/user is accessible with the dashboard’s Bearer partner_token (same auth as all other dashboard endpoints). Canvas uses Bearer access_token.
If there is a token-type restriction, BE should expose a lighter
PATCH /user/notification-emails endpoint instead:
PATCH /user/notification-emails
Body: { OPT_IN_INVOICE_NOTIFICATION?: boolean, OPT_IN_SCANNER_NOTIFICATION?: boolean, ... }
Response: { data: { notification_emails: Record<string, boolean> } }
13. company_details / admin company CRUD
Supabase tables: companies (admin mutations)
Why blocked: Canvas GET /public-companies exists (wired). Admin CRUD (create, update, delete
company records) has no canvas equivalent. AdminCompanies.tsx performs direct Supabase
insert/update/delete on the companies table.
Dashboard consumers: AdminCompanies.tsx (admin-only route /admin/companies)
Proposed REST endpoints:
POST /companies
Body: { name: string }
Response: { data: { id: string, name: string } }
PUT /companies/:id
Body: { name?, confidence_manual_override?, confidence_override_reason?,
confidence_notes?, sharing_enabled? }
Response: { data: Company }
DELETE /companies/:id
Response: 204
14. company_users — admin mutations
Supabase tables: company_users
Why blocked: Canvas has useSetUserStatusMutation, useDeleteUserMutation, and
useRestoreUserMutation but with different semantics than direct CRUD. There is no admin-style
user-create endpoint in canvas.
Dashboard consumers: AdminUsers.tsx, AdminCompanies.tsx
Operations the dashboard admin UI performs:
// Create user in a company
supabase.from("company_users").insert({ user_id, company_id, role })
// Update user role / status
supabase.from("company_users").update({ role, status }).eq("id", id)
// Delete user from company
supabase.from("company_users").delete().eq("id", id)
Ask: Clarify whether the existing canvas PUT /user/set-status and DELETE /user/:uid
endpoints cover these admin operations, or if a separate admin user-management endpoint is needed.
If the existing endpoints have company-scope restrictions, expose:
POST /company/users body: { email, role } (invite + assign)
PUT /company/users/:id body: { role?, status? }
DELETE /company/users/:id
15. facility_licenses
Supabase tables: facility_licenses
Why blocked: Admin-only licensing concept. No /facility-licenses endpoint anywhere in canvas.
Dashboard consumers: AdminFacilities.tsx, AdminLicensing.tsx
Lovable Supabase shape (key columns):
facility_licenses (
id uuid PRIMARY KEY,
facility_id uuid REFERENCES facilities(id),
license_type text, -- e.g. "organic", "fairtrade", "gots"
license_number text,
issued_at date NULLABLE,
expires_at date NULLABLE,
status text, -- "active" | "expired" | "pending"
created_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /facilities/:id/licenses
POST /facilities/:id/licenses body: { license_type, license_number, issued_at?, expires_at? }
PUT /facilities/:id/licenses/:license_id
DELETE /facilities/:id/licenses/:license_id
16. onboarding_tasks
Supabase tables: onboarding_tasks
Why blocked: Lovable-only admin onboarding wizard concept. No /onboarding-tasks endpoint in
canvas.
Dashboard consumers: AdminOnboarding.tsx
Lovable Supabase shape (key columns):
onboarding_tasks (
id uuid PRIMARY KEY,
company_id uuid REFERENCES companies(id),
task_type text, -- e.g. "setup_scanner", "invite_team", "first_audit"
completed boolean DEFAULT false,
completed_at timestamptz NULLABLE,
created_at timestamptz
)
Minimum endpoints needed (BE to define full spec):
GET /onboarding-tasks -- company-scoped
POST /onboarding-tasks/:id/complete
scanner_facility_assignments (PARTIAL — BE gap within an existing feature)
This is documented as part of the scanners PARTIAL contract but is worth calling out separately.
Why blocked: Canvas PUT /company/scanner/transfer/:id transfers a scanner to a user_id,
not a facility_id. The Lovable UI shows a “Facility” column on the scanner list — this requires
a scanner-to-facility assignment join that has no canvas equivalent.
Lovable Supabase shape:
scanner_facility_assignments (
id uuid PRIMARY KEY,
scanner_id text REFERENCES scanners(id),
facility_id uuid REFERENCES facilities(id),
assigned_at timestamptz
)
Proposed REST endpoints:
GET /company/scanner/assignments
Response: [{ id, scanner_id, facility_id, assigned_at }]
POST /company/scanner/assignments
Body: { scanner_id: string, facility_id: string }
Response: { data: ScannerFacilityAssignment }
DELETE /company/scanner/assignments/:id
Response: 204
Settings — field-level GUI-parity gaps (Lovable port, 2026-05-29 PM)
These are NOT new feature domains — they are individual fields the Lovable MVP renders on existing Settings screens that the current Fibretrace endpoints do not return. Each is a small additive field on an endpoint that already exists. The dashboard currently shows these rows empty (no fabricated data) pending BE. Listed here so they batch with the gaps above.
| # | Field | Dashboard consumer | Existing endpoint to extend | Suggested shape |
|---|---|---|---|---|
| S1 | billing_period | SettingsBilling (Billing period row) |
GET /company (or subscription/billing payload) |
billing_period: "monthly" | "annual" |
| S2 | payment_method | SettingsBilling (Payment method row) |
GET /company (or billing payload) |
payment_method: { brand: string, last4: string, exp?: string } |
| S3 | billing_address | SettingsBilling (Billing address row) |
GET /company (or billing payload) |
billing_address: { line1, line2?, city, state?, postcode, country } |
| S4 | invoice pdf_url | SettingsBilling invoice actions (View / Download) |
GET /company/invoices items |
add pdf_url: string per invoice row so View/Download can link |
| S5 | supply_chain_tier | SettingsCompany (Supply chain tier dropdown) |
GET /company |
supply_chain_tier: string (enum TBD by BE) |
Not pursued (security anti-pattern): SettingsApiKeys eye-toggle to reveal a full API key on
the list. BE only returns key_preview; the full key is shown once at creation. Revealing full
keys on a list endpoint is a deliberate security boundary — FE will NOT request this; the eye-toggle
stays disabled/hidden.
Already EXISTS — No BE work needed
The following items have confirmed Laravel REST equivalents and are either already wired or in-progress on the FE side. BE does not need to build anything new for these.
| Feature | Endpoint | Status |
|---|---|---|
| Invoices list | GET /company/invoices |
Wired and live |
| Invoice detail | GET /company/invoices/:code |
FE adding to slice |
| Notification preferences | GET /user + PUT /api/v2/user |
FE wiring |
| Public companies list | GET /public-companies |
Wired and live |
| Collections (orders) list | GET /orders |
Wired and live |
| Collection detail | GET /orders/:orderCode |
FE adding to slice |
| Scanners list | GET /company/scanners |
Wired and live |
| Scanner create/update/transfer | POST /company/scanner, POST /post-auditor-update-scanner, PUT /company/scanner/transfer/:id |
FE adding mutations |
| SAT audits (list) | GET /sat-audits |
Wired — awaiting BE confirmation it is live |
| Facilities list | GET /company/processes |
Wired and live |
| Notification templates (read) | GET /service-tab |
FE handling via static mapper |
Machine-readable source: audit.json in this directory. Frontend adapter layer: apps/dashboard/src/lib/apiAdapter.ts.